Oracle java存储过程中类解析的困惑

Posted

技术标签:

【中文标题】Oracle java存储过程中类解析的困惑【英文标题】:Confusion over class resolution in Oracle java stored procedures 【发布时间】:2009-02-27 23:03:28 【问题描述】:

我正在尝试在 oracle 中使用第三方 Java 库。该库似乎与我们的 Oracle 10g 服务器托管的相同 1.4 版本的 jvm 兼容,因为它在 Oracle 之外运行良好,所以我觉得我应该能够让它工作。这个库最终发出了基于 SOAP 的 http 请求,当我在 Oracle 中运行时,我得到了类解析错误。

这里有一行显示了差异:

Class msgfact = Class.forName("com.sun.xml.messaging.saaj.soap.MessageFactoryImpl");

我尝试使用 loadjava 实用程序将这些库注册到 Oracle 中,我得到了我认为成功的结果:

C:\>loadjava -verbose -schema MYUSER -user MYUSER/MYPWD@dbinstance -force saaj-impl.jar

看起来一切都已加载,我可以在该列表中看到这个 MessageFactoryImpl 类。但后来我尝试从 Oracle SQL 运行这行代码(在我编写并加载了 loadjava 的另一个类中),这一行抛出 ClassNotFoundException (java.lang.ClassNotFoundException: com/sun/xml/messaging/saaj/soap/MessageFactoryImpl )。

然后我回去尝试在 loadjava 命令行中添加“-resolve”开关。就像这些 saaj 类正在注册一样,但它们没有正确解析。

我怎样才能成功地将这些 saaj 类导入 Oracle,或者如果由于某种原因 Oracle 已经加载了这些,我如何才能说服我自己的代码成功使用现有的类?

FWIW,我已经采取措施确保授予了适当的套接字权限,并且我的代码可以成功地向目标 url 发出通用 http 请求。只是很难使用库的 SOAP 堆栈来实现它。

编辑: 这是我的 loadjava 结果示例。这似乎准确地显示了失败的原因,但我很困惑为什么这些特定类在预解决步骤中似乎得到正确处理时没有得到解决。我在这里删除了大约 80% 的文件,但还有其他类显示相同的类解析问题。

参数:'-verbose''-schema''MYSCHEMA''-user''MYSCHEMA/MYSCHEMA@actest''-resolve''-force''saaj-impl.jar' [剪辑] 创建:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/EnvelopeFactory 加载:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/EnvelopeFactory 创建:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/GifDataContentHandler 加载:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/GifDataContentHandler 创建:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/JpegDataContentHandler 加载:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/JpegDataContentHandler 创建:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageFactoryImpl 加载:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageFactoryImpl 创建:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl 加载:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl [剪辑] 解决: MYSCHEMA.com/sun/xml/messaging/saaj/soap/AttachmentPartImpl 类 解决:类MYSCHEMA.com/sun/xml/messaging/saaj/soap/Envelope 解决:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/EnvelopeFactory 错误:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/EnvelopeFactory ORA-29534: 引用的对象 MYSCHEMA.com/sun/xml/messaging/saaj/soap/SOAPPartImpl 无法解析 解决: MYSCHEMA.com/sun/xml/messaging/saaj/soap/GifDataContentHandler 类 解决: MYSCHEMA.com/sun/xml/messaging/saaj/soap/JpegDataContentHandler 类 解决: MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageFactoryImpl 类 错误:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageFactoryImpl ORA-29534: 引用的对象 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl 无法解析 错误:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl ORA-29534: 引用的对象 MYSCHEMA.com/sun/xml/messaging/saaj/soap/impl/EnvelopeImpl 无法解析 错误:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl$1 ORA-29534: 引用的对象 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl 无法解析 跳过:类 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl$2 [剪辑] 以下操作失败 MYSCHEMA.com/sun/xml/messaging/saaj/soap/EnvelopeFactory 类:分辨率 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageFactoryImpl 类:分辨率 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl 类:分辨率 MYSCHEMA.com/sun/xml/messaging/saaj/soap/MessageImpl$1 类:分辨率 [剪辑] exiting :处理过程中发生故障

【问题讨论】:

【参考方案1】:

首先,在 Oracle 外部验证您要查找的类实际上在该 jar 文件中。我希望它是,但检查并没有什么坏处。

然后,我会检查已加载哪些类以及它们是否具有有效状态。

SELECT object_name, dbms_java.longname(object_name), status
  FROM user_objects
  WHERE object_type='JAVA CLASS'
  ORDER BY 1

如果有很多类,你可能想稍微限制一下,例如:

WHERE dbms_java.longname(object_name) LIKE '%MessageFactoryImpl'

如果类不存在,或者包名错误,则问题出在 loadjava 命令。

如果类存在但其状态为无效,则检查 USER_ERRORS 以查看错误是什么。我不记得我是否在 Oracle 中完成了动态类加载,但我记得静态链接会产生错误,暗示一个类在真正存在但有错误时并不存在。

loadjava 输出发布后的新信息

loadjava 输出似乎与您在数据库中查找类的尝试不一致。如果它正在加载但未解决,它仍应列出,但状态为 INVALID。

我得到了 JAR,并在一个空模式中自己尝试了它。类加载但按预期无效:

dev> select object_name, dbms_java.longname(object_name),status
  2  from user_objects
  3  where object_name like '%MessageFactoryImpl';

OBJECT_NAME
--------------------------------------------------------------------------------
DBMS_JAVA.LONGNAME(OBJECT_NAME)
--------------------------------------------------------------------------------
STATUS
-------
/3e484eb0_MessageFactoryImpl
com/sun/xml/messaging/saaj/soap/MessageFactoryImpl
INVALID

然后我检查了课堂上的错误:

dev> alter java class "com/sun/xml/messaging/saaj/soap/MessageFactoryImpl" resolve;
dev> /

Warning: Java altered with compilation errors.

dev> show error
Errors for JAVA CLASS "/3e484eb0_MessageFactoryImpl":

LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0      ORA-29521: referenced name javax/xml/soap/MessageFactory could
         not be found

0/0      ORA-29521: referenced name javax/xml/soap/SOAPMessage could not
         be found

0/0      ORA-29521: referenced name javax/xml/soap/MimeHeaders could not
         be found

0/0      ORA-29521: referenced name javax/xml/soap/SOAPException could not
         be found

(这些错误也将列在 USER_ERRORS 中,假设至少尝试了一次解决方法。)

很明显,这个类引用了基础 SOAP 库中的类。你也必须加载它——你这样做了吗?

仅供参考,当我编写一些代码来调用 Class.forName() 时,我确实得到了 ClassNotFoundException。这似乎违反直觉,因为类对象确实存在于模式中。但是,从 Oracle 中的类加载器的角度来看,一个无效的类并不真正“存在”。为了使该类有效,Oracle 必须能够解析其对其他类的所有引用。

【讨论】:

saaj-impl.jar 文件确实包含 com.sun.xml.messaging.saaj.soap.MessageFactoryImpl 类。感谢您使用“longname”功能。我不知道如何找回那些全名!在我的 Oracle 实例中没有类似于“%MessageFactory%”的长名称类。 因此,很明显 loadjava 并没有按照我的意愿去做。你有什么想法可以调试我的使用吗? 我希望 loadjava 的详细输出至少包含一些指示。你能发布一个有代表性的输出样本吗? 已编辑答案。在这一点上,我最好的猜测是您还没有加载包含由 SAAJ 类引用的类的其他 jar。【参考方案2】:

这是您第一次在数据库上执行 Java 吗?这是 hello world 实现,以确保您的 Oracle JVM 正常运行并且您拥有必要的权限。你说“我的数据库架构,所以我可以做任何我想做的事”——并不意味着你有适当的授权。

SQL> create or replace and compile java source named "Hello" as 
   public class Hello 
      public static String world()  
         return "Hello World "; 
       
   ; 
/ 

Java created. 

现在是包装函数

SQL> create or replace function Hello RETURN VARCHAR2 
     as LANGUAGE JAVA NAME 'Hello.world() return String'; 
     / 

Function created.

现在测试一下

SQL> select Hello from dual; 

HELLO 
----------------------------------- 
Hello World 

【讨论】:

哦,我可以运行其他方法就好了。当我说我可以通过 http 访问目标 url 时,我试图在我的消息中这么说。那是在我在 oracle 中运行的 java 类中。这是我的hello world。【参考方案3】:

这样做:

try

    System.out.println("Class.forName returned: " + 
        Class.forName("com.sun.xml.messaging.saaj.soap.MessageFactoryImpl"));

catch(final Throwable ex)

    ex.printStackTrace();
    System.exit(1);

只是为了 100 和 10% 确定它没有抛出以某种方式被隐藏的异常并且它确实返回 null。如果还是这样,请告诉我(如果上面的代码有效,但返回 null,这是一个有趣的问题)。

【讨论】:

嘘。我的错。 Class.forName("com.sun.yada.yada..") 行抛出异常:java.lang.ClassNotFoundException: com/sun/xml/messaging/saaj/soap/MessageFactoryImpl【参考方案4】:

尝试 #3 :-)(我不使用 Oracle...但这是一个很好的调试问题...)

这里关于 Oracle 中 Class.forName 的信息有帮助吗?

http://download.oracle.com/docs/cd/B14117_01/java.101/b12021/appover.htm#i1006547

这表现为 ClassLoader 问题,因此希望该解决方案与“真实的、非 Oracle”世界中发生的情况相同 :-)

编辑...再试一次...

好的,再看一下……下面的输出是什么:

System.out.println("vm vendor:     " + System.getProperty("java.vendor"));
System.out.println("vm version:    " + System.getProperty("java.version"));
System.out.println("class version: " + System.getProperty("java.class.version"));

我想知道类文件版本是否存在问题 - 类文件是否有无法在 Oracle VM 上运行的版本?

【讨论】:

是的,这是我的数据库模式,所以我可以为所欲为。问题是我不一定知道自己在做什么!【参考方案5】:

类解析和加载是由 VM 处理的复杂操作。规范中描述的这两个操作要使用的算法对 VM 实现者是开放的。 我的感觉是,在您的情况下,该解决方案作为操作仅检查类本身的可用性,而稍后在尝试有效加载类时失败(很可能是由于缺少依赖项:加载类时,VM 需要至少解决对其他类的所有直接引用)。 您必须确保 Oracle 可以使用所有类,并且在 Google 上快速搜索 ORA-29534 会发现很多人都遇到了这个问题(我很确定有人已经弄清楚了)。

./亚历克斯

【讨论】:

【参考方案6】:

saaj-impl.jar 是 Web Services Developer Pack 的 SAAJ 组件的一部分。它依赖于 SAAJ 附带的其他 JAR,例如,据我所知,它肯定依赖于 saaj-api.jar 和 activation.jar。当然,saaj-api.jar 也必然会依赖很多其他的 JAR。

就 JWSDP 1.5 而言,您可以在 JWSDP 1.5 Release Notes 中找到有用的信息。 JWSDP 1.6 在 SAAJ 中有不同的 JAR 我还没有发现 JWSDP 2.0 发行说明在这方面特别有用。顺便说一句,JWSDP 2.0 会在类路径中放置不同的 JAR;所以问题的范围最终取决于您使用的 saaj-impl.jar 版本。

以防万一您可能需要它,以下页面中已经提供了一些关于如何将 SOAP 客户端 JAR 加载到 Oracle 数据库中并使用它们的文档。不过,我认为这些与您的库附带的 SAAJ JAR 不同。

Calling a webservice from the database。 How to install the SOAP client stack in the Oracle database。 Install JAR files in the database

【讨论】:

以上是关于Oracle java存储过程中类解析的困惑的主要内容,如果未能解决你的问题,请参考以下文章

如何在ORACLE存储过程中调用WEBSERVICE函数

java中如何获取oracle存储过程返回的多个值。

java字符串数组作为参数传递oracle存储过程

如何在Oracle中使用Java存储过程

如何在存储过程(Oracle)中使用 SELECT 请求?

oracle 存储过程