【问题标题】:The symbol you provided is not a function您提供的符号不是函数
【发布时间】:2011-09-14 20:54:16
【问题描述】:

进一步阅读之前的要点

  1. 所有变量均已正确更改(您必须相信我)
  2. 作用域不会在这些长时间运行的进程发生时被重置
  3. 在为所谓的缺失/无效方法转储元数据时,我得到了正确的信息
  4. 在应用程序中只有两个地方引用了此方法的名称。一次是在哪里定义的,一次是在下面的代码中调用该方法。

我有一个非常奇怪的间歇性错误,我似乎无法追踪。这是背景(为了简化发布,这些都被严重删减了)。

FeedService.cfc:

<cfcomponent output="false" extends="FeedDAO">

    <cffunction name="processXmlFile" access="public" output="false" returntype="struct">
        <cfset Var local = StructNew() />

/***************************************
THE VARIABLES ARE ALL VAR'D - PROMISE!!!
Lots of other stuff goes on in here to get the ultimate set of XML nodes to loop through
*****************************************/

        <cfloop from="1" to="#ArrayLen(local.arrChannels)#" index="local.currentChannelItem">
            ... Lots of XML parsing and stuff and things going on here ...

            <cfset LOCAL.invCheck = checkCustomerListing(
                Acct_ID = local.invStruct.AcctID
                , CustomerListingID = local.invStruct.CustomerListingID
            ) />

            ... Lots more stuff going on here ...

        </cfloop>
    </cffunction>
</cfcomponent>

FeedDAO:

<cfcomponent output="false">

    <cffunction name="checkCustomerListing" access="public" output="false" returntype="numeric" hint="Returns the numeric inventory ID for an existing inventory listing, or 0 if the listing doesn't exist.">
        <cfargument name="Acct_ID" type="numeric" required="true" hint="" />
        <cfargument name="CustomerListingID" type="string" required="true" hint="" />
        <cfset var rs = "">

        <cfquery name="rs" datasource="#Variables.DSNs.Primary#">
            SELECT ID FROM TheTable
            WHERE
                Acct_ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#Arguments.Acct_ID#" />
                AND Customer_Listing_ID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Arguments.CustomerListingID#" />
        </cfquery>

        <cfif rs.RecordCount>
            <cfreturn rs.Inv_ID />
        <cfelse>
            <cfreturn 0 />
        </cfif>

    </cffunction>

</cfcomponent>  

我这样调用初始函数:

<cfset processStruct = Server.FeedService.processXmlFile(filePath) />

因此,当提要提交给 processXMLFile 函数时,它会查看文件中的所有项目。一个提要文件可能有 10、100 甚至 1000 个条目。在处理文件时,我偶尔会收到类似的错误消息:

[struct]
Detail: The symbol you provided checkCustomerListing is not the name of a function.
Message: Entity has incorrect type for being called as a function.
StackTrace: coldfusion.runtime.CfJspPage$UninvocableEntityException: Entity has incorrect type for being called as a function.
    at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:2441)
    at coldfusion.runtime.SuperScope.invoke(SuperScope.java:18)
    at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2222)

更多堆栈转储信息

    Type: Application
    symbolName: checkCustomerListing

    [object of coldfusion.runtime.CfJspPage$UninvocableEntityException]
    Class Name: coldfusion.runtime.CfJspPage$UninvocableEntityException
    Fields: 
        java.lang.String symbolName: checkCustomerListing
    Parent Class: [object of coldfusion.runtime.ApplicationException]
        Class Name: coldfusion.runtime.ApplicationException
        Parent Class: [object of coldfusion.runtime.NeoException]
            Class Name: coldfusion.runtime.NeoException
            Methods:  
                findAdvancedCFTarget(coldfusion.runtime.AdvancedCFException, java.lang.String[]) returns int 
                findCustomTarget(coldfusion.runtime.CustomException, java.lang.String[]) returns int 
                findThrowableTarget(java.lang.Throwable, java.lang.String[]) returns int 
                getDetail() returns java.lang.String 
                getLocalizedMessage() returns java.lang.String 
                getMessage() returns java.lang.String 
                getRootCause() returns java.lang.Throwable 
                getString(java.lang.Throwable, java.lang.String, java.util.Locale) returns java.lang.String 
                getType() returns java.lang.String 
                setLocale(java.util.Locale) returns void 
                unwrap(java.lang.Throwable) returns java.lang.Throwable
            Parent Class: [object of java.lang.RuntimeException]
                Class Name: java.lang.RuntimeException
                Parent Class: [object of java.lang.Exception]
                    Class Name: java.lang.Exception
                    Parent Class: [object of java.lang.Throwable]
                        Class Name: java.lang.Throwable
                        Methods:  
                            fillInStackTrace() returns java.lang.Throwable 
                            getCause() returns java.lang.Throwable 
                            getLocalizedMessage() returns java.lang.String 
                            getMessage() returns java.lang.String 
                            getStackTrace() returns java.lang.StackTraceElement[] 
                            initCause(java.lang.Throwable) returns java.lang.Throwable 
                            printStackTrace(java.io.PrintWriter) returns void 
                            printStackTrace(java.io.PrintStream) returns void 
                            printStackTrace() returns void 
                            setStackTrace(java.lang.StackTraceElement[]) returns void 
                            toString() returns java.lang.String

我可能会在 1000 个条目中遇到一个错误,或者我可能会一次收到一小批错误,而其余的提要处理都很好(由于一些 try/catch 逻辑可以防止整个事情搞砸)。在某一时刻,checkCustomerListing 在一个完全不同的服务器范围对象中,我从来没有遇到过问题。我将它移到 FeedDAO 并开始通过 Super 范围调用它,这就是这些间歇性错误开始的时候。

更新:我已经正确地修改了所有内容,为了简洁起见,我只是将其全部删除。

再次更新:更改了代码示例 cmets,以明确在第一个循环开始之前有很多内容正在进行,包括设置所有 LOCAL 变量在循环中使用。

更多代码信息: 我应该注意到,在我们的整个应用程序中(成千上万行代码)中只有两个地方存在字符串“checkCustomerListing”。一是调用函数的地方,二是声明函数的地方。在任何地方都没有字符串 checkCustomerListing 的其他实例。

更新:2011 年 9 月 6 日

我添加了一些额外的错误检查,看看我是否可以找出应用程序认为 checkCustomerListing 是什么(感谢 Adam 和 Ryan)。这是我的新 try/catch 语句:

<cfcatch type="any">
    <cfset local.tmpError.cfcatch = cfcatch>

    <cfif isDefined("checkCustomerListing")>
        <cfset local.tmpError.customerListing = checkCustomerListing />
        <cfset local.tmpError.customerListingMeta = getMetaData(checkCustomerListing) />
    <cfelse>
        <cfset local.tmpError.customerListing = "Checkcustomerlisting is not defined" />
    </cfif>

    <cfset Server.Utilities.Errors.emailCaughtError(local.tmpError)>

</cfcatch>

所以今天早上报错了,在我收到的邮件中,转储中没有customerListing节点,但是有一个元节点:

CUSTOMERLISTINGMETA:  
    [struct]
    ACCESS: public
    HINT: Returns the numeric inventory ID for an existing inventory listing, or 0 if the listing doesn't exist.
    NAME: checkCustomerListing
    OUTPUT: false
    PARAMETERS:  
        [array]
        1) [struct]
            HINT: [empty string]
            NAME: Acct_ID
            REQUIRED: true
            TYPE: numeric 
        2) [struct]
            HINT: [empty string]
            NAME: CustomerListingID
            REQUIRED: true
            TYPE: string 
    RETURNTYPE: numeric

所有元信息完全正确正确......所以如果它可以找到函数的元数据,为什么它不能找到函数本身?

【问题讨论】:

  • 我不知道这是否是您帖子缩减的一部分,但 local.arrChannels 是在哪里声明的?
  • 您能告诉我们您正在运行什么版本的 ColdFusion。一些 cmets 指的是 varring,但如果您运行的是 ColdFusion 9,那么 local 是一个有效范围,不需要任何范围。
  • James,这个问题已经被标记为“coldfusion-8”,这应该可以为 Dan 使用的版本提供一个小线索。 ;)
  • 通过函数开头的 XML 解析。唯一的问题是它认为函数不存在。如果我有未声明的变量,它甚至永远不会归结为问题部分。
  • 您是否尝试过在错误处理中的某处对 checkCustomerListing 进行转储,以便在抛出错误时准确查看 checkCustomerListing 是什么?听起来肯定有什么东西正在影响指向该函数的变量中的东西。

标签: coldfusion coldfusion-8


【解决方案1】:

正如其他人所暗示的那样,这通常是由于缺乏 VARing 而出现的。人们经常有一个名为“getStuff”的私有函数,其中有一个名为“getStuff”的查询。如果变量不是 VARed,则 getStuff 查询结果进入 CFC 的变量范围,这将覆盖函数 getStuff,因为它也位于变量范围内。

因此,请检查您对 checkCustomerListing 的使用,以查找任何非 VARed(或在本地范围内)的同名变量。

(请注意,checkCustomerListing 变量不需要在同一方法中发生这种情况......它可以在 CFC 或任何扩展或超级 CFC 中的任何位置......)

【讨论】:

  • 好点,但我确实已经声明了所有内容,而且这个问题只会在处理更大文件的过程中小批量发生。在通过提要的 1000 次循环中,它可能仅在一项上出错,或者可能在连续 10 次上出错。错误的数量甚至会从一个调用变为另一个调用,每次处理同一个文件。
  • 嗯。这是在实验室环境中,这是唯一运行的请求吗?还是有其他的干扰?
  • 这是在生产环境中,可能有其他东西同时运行。但是,这里的所有部分都是非常独立的。在我看来,CF 正在失去对该函数的引用,但我不知道如何判断这是如何发生的。
  • 所以你不能在 dev 中复制这个?
  • 我没有在我的本地 dav 系统上看到这个问题。我刚刚连续两次处理了一个 7.8 兆的 XML 文件,没有发现任何问题。我什至尝试通过两个单独的线程同时处理同一个文件,但仍然没有看到问题...
【解决方案2】:

你为什么要做 super.checkCustomerListing?只有在您覆盖服务中的功能并想要运行“父级”时,您才会这样做。只需调用 checkCustomerListing()。

【讨论】:

  • 我会试一试,但不确定如何解决这个问题。 Super总是不应该存在吗?无论我是否使用Super 范围,它都应该始终存在。
  • 虽然这是个好建议,但与这个问题无关,是吗?
  • 这与问题完全有关,我只是不知道如何解决它。问题特别是子对象失去它正在扩展的父对象中checkCustomerListing函数的钩子......
  • 这不是你得到的错误,如果它以某种方式发生,你只会得到“找不到 checkCustomerListing 方法”。尽管如此,显然这里正在发生一些不明显的事情,所以我认为人们应该期待“意外行为”。
  • 那么 - 你删除了 super.checkCustomerListing() 吗?我看你还有。
【解决方案3】:

它可能在您的删节代码中,但您是否验证 local.invStruct.AcctID 和 local.invStruct.CustomerListingID 都存在并且类型正确?有时在 Java 中“函数不存在”意味着“方法签名不存在”。我不确定 CF 何时验证数据类型,但如果您在相同的两个变量上循环,是否可能是快捷方式而不是在以后的循环中验证类型。

为了以防万一,还可以在返回值上扔一个 val()。

【讨论】:

  • 好主意,但是是的,两个变量都被声明了。在我收到的错误电子邮件中,我已转储了整个 instruct 变量,AcctIDcustomerListingID 都在其中:/
  • 仍然值得在几个地方扔一个 val。可能无法解决问题,但不会受到伤害。
  • 没错,当然不会受伤。
【解决方案4】:

当您将对象缓存在需要一段时间才能运行的范围内并且您覆盖或删除缓存中的对象时,我之前已经看到过这种情况。我的猜测是这就是正在发生的事情,另一个应用程序正在对缓存中的对象执行某些操作,这导致其他应用程序相互绊倒。

一个很好的测试方法是将缓存在服务器范围内的对象复制到一个局部变量,然后使用该局部变量来运行你的进程:

<cfset _feedservice = duplicate(Server.FeedService)>
<cfset processStruct = _feedservice.processXmlFile(filePath) />

现在,即使服务器范围内的对象被覆盖或删除,您仍然拥有您的进程将使用的原始对象的副本。在上面的代码周围使用锁也不是一个坏主意:

<cflock scope="server" timeout="5" type="readonly">
<cfset _feedservice = duplicate(Server.FeedService)>
<cfset processStruct = _feedservice.processXmlFile(filePath) />
</cflock>

现在个人更好的做法(在我看来)是向您的 onApplicationOnStart() 事件添加一些内容,该事件会将您缓存到服务器范围的所有对象复制到应用程序范围中。我可以看到任何人使用服务器范围的唯一原因是因为您希望服务器上的所有应用程序共享相同的对象。这样做仍然允许您的应用程序共享相同的代码,但会防止其他应用程序意外地相互绊倒

【讨论】:

  • 就描述症状而言,这是一个很好的调用,但在这种情况下,这是服务器上的唯一应用程序......话虽如此,这是一个很长的-运行应用程序,所以它可能仍然与此有关。我在 Adam 的建议下添加了一些额外的调试,一旦我从中获得更多信息,我很可能会按照你的建议作为下一步。
  • 我也有同样的想法,并试图复制它。我设计了一个测试服务器范围的 C.cfc 的 CFC 实例,它扩展了 SuperC.cfc,在 C.cfc 中创建了一个需要 10 秒运行的方法(通过在其中放置一个 sleep()),然后调用一个超级范围方法。在 10 秒内,我杀死了服务器范围内的 CFC 实例。该方法完成得很好。所以我改为将服务器范围的实例重新创建为同一个 CFC 的新实例:它仍然没有中断。然后我将它重新创建为一个完全不同的 CFC 的实例:仍然没有问题。 Java 太聪明了,不能让这成为一个问题。
猜你喜欢
  • 2011-12-03
  • 1970-01-01
  • 2021-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多