【问题标题】:Getting ColdFusion-Called Web Service to Work with JavaLoader-Loaded Objects让 ColdFusion 调用的 Web 服务与 JavaLoader 加载的对象一起工作
【发布时间】:2013-03-26 10:06:24
【问题描述】:

是否可以使用 JavaLoader 来获取由 CF 调用的 Web 服务返回的对象,并且 JavaLoader 加载的对象是相同的类路径上下文?我的意思是,没有很多困难?

// get a web service
ws = createObject("webservice", local.lms.wsurl);
// user created by coldfusion
user = ws.GenerateUserObject();
/* user status created by java loader.
** this api provider requires that you move the stubs
** (generated when hitting the wsdl from CF for the first time)
** to the classpath.
** this is one of the stubs/classes that gets called from that.
*/
UserStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
// set user status: classpath context clash
user.setStatus(UserStatus.Active);

错误:

  • 详细信息:要么没有具有指定方法名称的方法,要么 参数类型或 setStatus 方法被参数重载 ColdFusion 无法可靠破译的类型。冷融合 找到 0 与提供的参数匹配的方法。如果这是一个 Java 对象 并且您验证了该方法存在,请使用 javacast 函数 减少歧义。
  • 消息:未找到 setStatus 方法。
  • 方法名设置状态

尽管表面上调用匹配用户上的方法签名--setStatus(com.geolearning.geonext.webservices.Status)--该类位于不同的类路径上下文中。这就是我收到上述错误的原因。

【问题讨论】:

  • (编辑)哎呀,我的意思是说你有没有尝试设置parentClassLoader
  • 那会是什么样子?对于这个 n00b,一些“仅供专家使用”的东西没有得到足够好的记录。
  • 其实再看the API,最简单的方法就是设置loadColdFusionClassPath=true注意 - 将 loadColdFusionClassPath 设置为“true”时,此值将被 ColdFusion 类加载器覆盖。这是一个快捷方式:javaLoader.init(loadPaths=arrayOfJars, parentClassLoader=getPageContext().getClass().getClassLoader());
  • 这似乎没有帮助。我认为问题仍然存在:虽然 UserStatus 知道 CF 类路径(以及加载的类路径),但我认为没有任何方法可以让用户对象知道加载的类路径。所以我认为我们陷入了同样的困境。 (如果这听起来不对,请纠正我——我正在编造这些东西。)
  • 嗯.. 听起来方法调用被拒绝了,因为UserStatus 是由不同的类加载器创建的。即类加载器是出了名的挑剔。它可以是正确的类,但如果它是由 不同的 类加载器创建的,它将无法被识别。如果这是问题所在,我认为共享父类加载器应该解决它。也就是说,你有什么理由不能只使用createObject 来处理所有事情?此外,这是我们可以访问/测试的公共网络服务吗?

标签: web-services coldfusion classpath coldfusion-9 javaloader


【解决方案1】:

Jamie 和我在此离线工作并想出了一个创造性的解决方案 :)

(对于冗长的回答,我深表歉意,但我认为对于那些发现类加载器和我一样令人困惑的人来说,有必要进行一些解释。如果您对“为什么”方面不感兴趣,请随时跳到结束)。

问题:

问题肯定是由于多个类加载器/路径造成的。显然 CF Web 服务使用 dynamic URLClassLoader(就像 JavaLoader 一样)。这就是它可以即时加载生成的 Web 服务类的方式,即使这些类不在核心 CF“class path”中。

(基于我有限的理解...)Class loaders 遵循层次结构。当涉及多个类加载器时,它们必须遵守一定的规则,否则它们将无法很好地配合使用。规则之一是子类加载器只能“看到”由祖先(父母、祖父母等)加载的对象。他们看不到由兄弟加载的类。

如果您检查由 JavaLoader 创建的对象和由createObject 创建的另一个对象,它们确实是兄弟姐妹,即 CF 引导类加载器的两个孩子。所以一个不会识别另一个加载的对象,这可以解释为什么setStatus调用失败。

鉴于子可以看到父加载的对象,显而易见的解决方案是更改对象的构造方式。构造调用,使一个类加载器最终成为另一个类加载器的父级。奇怪的是,事实证明这比听起来更棘手。尽管尝试了多种组合(包括使用switchThreadContextClassLoader 方法),但我找不到实现这一目标的方法。

解决方案:

最后我有一个疯狂的想法:不要加载任何 jars。只需将 Web 服务的加载程序用作 parentClassLoader。它已经在自己的“类路径”中拥有所需的一切:

    // display class path of web service class loader
    dynamicLoader = webService.getClass().getClassLoader();
    dynamicClassPath = dynamicLoader.getURLS();
    WriteDump("CLASS PATH: "& dynamicClassPath[1].toString() );

JavaLoader 将自动将它找不到的类的调用委托给parentClassLoader - 和宾果游戏 - 一切正常。不再有类加载器冲突。

    webService = createObject("webservice", webserviceURL, webserviceArgs);
    javaLoader = createObject("component", "javaloader.JavaLoader").init(
            loadPaths = [] // nothing
            , parentClassLoader=webService.getClass().getClassLoader()
        );

    user = webService.GenerateUserObject();
    userStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
    user.setStatus(userStatus.Active);
    WriteDump(var=user.getStatus(), label="SUCCESS: user.getStatus()");

【讨论】:

  • 是的,效果很好,并且有很多令人愉快的副作用:我们不再需要复制存根;我们不必为不同的 API 环境保留不同的存根副本;当 API 发生变化时,我们不必升级存根。谢谢,利。
  • 注意:此解决方案在 Railo/Lucee 中不起作用,因为无法将 classLoader 从其 webService 对象中取出:luceeserver.atlassian.net/browse/LDEV-169
  • 你不能在对象上调用getClass()?多么奇怪。我没有太多使用Lucee。想知道这背后的原因是什么..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多