我的Java知识库
随笔 - 21, 文章 - 10, 评论 - 4, 引用 - 0
数据加载中……

websphere部署时报java.lang.NoClassDefFoundError: org.jdom.Content

我的项目在RAD编译的时候,没有出错,但是在websphere部署的时候总是提示java.lang.NoClassDefFoundError: org.jdom.Content
但是我已经在项目引用的时候已经import了,要不然也不能正确编译。
这个问题困扰了我两天,最后在网上搜索的时候,一个大牛的发言提示了我。他的大体意思是这个问题是jdom.jar引用冲突造成的,所以我就在was的lib文件夹中删除了jdom.jar,果然it works。
希望跟我有同样问题的人能看到,少浪费点时间吧。

http://jspwiki.org/wiki/BugPluginManagerParseErrorInWebSphere

posted @ 2007-03-23 15:53 yangrui 阅读(3677) | 评论 (0)编辑 收藏

J2EE 类装入揭密

J2EE 类装入揭密

developerWorks

 

Tim deBoer (deboer@ca.ibm.com), WebSphere Studio 开发团队, IBM 多伦多实验室
Gary Karasiuk (karasiuk@ca.ibm.com), Java 解决方案设计师, IBM 多伦多实验室

2002 年 1 月 01 日

本文解释 J2EE 和 WebSphere Application Server 用来构建和装入类的复杂技术。学习当使用 WebSphere Studio Application Developer 构建项目时如何创建大部分 J2EE 规范以及如何避免 ClassNotFoundException。

©2001 International Business Machines Corporation. All rights reserved.

简介

一般 J2EE 尤其是 WebSphere® Application Server 使用复杂的技术来构建和装入类。象许多开发者一样,您可能想知道它们是如何组织在一起的,如何设计项目才能节省时间并且充分利用开发环境。

本文提供有关 J2EE 规范以及如何使用它在 WebSphere Studio Application Developer 中构建项目的信息。除了创建基本的 J2EE 应用程序,我们还将研究一些 Application Developer 的最佳实践和高级功能部件。最后,我们将为您提供一个扎实的基础以便处理“可怕的”ClassNotFoundException。





回页首


J2EE 模块

J2EE 规范描述三种类型的模块:Web 模块、EJB 模块和应用程序客户机模块。当部署到 J2EE 应用程序服务器时,通常会将所有这些模块都压缩到单个 J2EE 应用程序 EAR 文件中。在以下每一节中会讨论一种模块以及如何使用 Application Developer 来构建它。





回页首


Web 模块

Web 模块包含 HTML、图像、JSps™、Java™类和 servlet,以及创建 Web 应用程序所需的所有其它资源。象其它模块一样,Web 模块包含一个部署描述符。在 Web 模块中,部署描述符 web.xml 具有 servlet 初始化和映射信息以及用于在应用程序服务器中运行 Web 模块的其它设置。

Web 模块有两个特殊的 Java 代码文件夹: WEB-INF/classesWEB-INF/lib 。classes 文件夹可以包含“松散(loose)”Java 类(不在 JAR 文件中的类),并且可以将它用于 Web 应用程序范围内的 servlet 或实用程序类。通常对于这个文件夹使用特殊的类装入器,因此如果对类进行更改,则应用程序服务器会自动将它们重新装入。lib 文件夹可以包含也是由 Web 应用程序使用的 JAR 文件(而不是 ZIp 文件!)。应该将第三方 JAR 文件和其它实用程序 JAR 文件放入这个文件夹。如果 JAR 文件被其它 Web 或 EJB 模块使用,则按照下面的 企业应用程序一节中的说明将它们移动到“企业应用程序”项目中。

在 Application Developer 中,Web 模块由 Web 项目表示,它包含两个文件夹:source 和 webApplication。webApplication 文件夹包含扩展形式的完整 J2EE Web 模块。source 文件夹用于存放 .java 文件,因为它们常常不是部署的 Web 模块的一部分。当您在这个文件夹中创建 Java 资源时,会自动编译它们并将它们放入 webApplication/WEB-INF/classes 文件夹。这会使 Web 项目始终保持同步,并准备好测试或导出。

如果从 WAR 文件导入了 Web 模块,则可能注意到 lib 文件夹中的 projectname_classes.jar 文件。这个文件包含导入的 WAR 文件的原始内容。如果 WAR 文件包含源代码,则删除该文件,因为类将在 classes 文件夹中冗余地出现。





回页首


EJB 模块

EJB 模块包含 EJB bean、其特定于服务器的部署代码、部署描述符和助手类(可选的)。它们包含应用程序的业务逻辑,并且在一般情况下由 Web、Application Client 和其它 EJB 模块调用。

在 Application Developer 中,EJB 模块由 EJB 项目表示。这些项目还有两个文件夹,bin 和 ejbModule。EJB 模块的源代码保存在 ejbModule 文件夹中。当更改和生成部署代码时,将这个文件夹中的 Java 类编译到 bin 文件夹中。将剩余的资源(例如,部署描述符)也复制到 bin 文件夹中。与 Web 项目的 webApplication 文件夹类似,bin 文件夹总是包含完整的已部署的 EJB 模块。与 Web 项目不同的是,不应该以任何手工方式修改 bin 文件夹,否则可能丢失更改。在 ejbModule 文件夹中进行所有更改,将会自动编译这些更改或将它们复制到 bin 文件夹中。

如果从 EJB JAR 文件中导入 EJB bean,则可能注意到位于您项目根目录中的 Xxx_importedClasses.jar 文件。这个文件包含导入的 EJB JAR 文件的原始内容。如果 JAR 包含源代码,则删除该文件,因为类将在 bin 文件夹中冗余地出现。





回页首


应用程序客户机模块

使用应用程序客户机模块以包含全功能客户机 Java 应用程序(非基于 Web),它连接到并使用在服务器中定义的 J2EE 资源。通过将客户机代码放入应用程序客户机模块而不是简单 JAR 文件,应用程序客户机将得益于服务器的资源(它不需要将类路径重新指定到 J2EE 和服务器 jar 文件)以及更方便的 JNDI 查询(服务器填充初始上下文和其它参数)。

在 Application Developer 中,应用程序客户机模块由应用程序客户机项目表示。对于大部分模块来说,可以象在 Java 项目中创建独立的 Java 应用程序一样工作。





回页首


企业应用程序(J2EE 应用程序)

企业应用程序是一个或多个 Web、EJB 或应用程序客户机模块的组合。作为这些其它模块的超集,它可以包含可能是多个模块组合的完整的应用程序。除了是一个有效的组合机制外,企业应用程序还在完整的应用程序级别上部署和维护代码,与作为单个代码片段相比,这更加容易。企业应用程序也可以重设被包含的模块的部署描述符内部的设置,以更实用的方式来组合或部署它们。

企业应用程序可能包含所含模块使用的 JAR 文件。这允许在应用程序级别上共享代码,并且还是放置由多个 Web 或 EJB 模块使用的实用程序 JAR 文件的最佳位置。通过将这些 JAR 文件放入企业应用程序而不是全局类路径,它们同时也符合 J2EE 规范,在移动到新服务器时不需要特殊发布和设置。

在 Application Developer 中,企业应用程序是由企业应用程序项目表示的。因为没有直接将源代码构建到企业应用程序,所以这些项目没有子文件夹。





回页首


WebSphere 类装入器

WebSphere Application Server 使用几个类装入器来遵循 J2EE 规范。除了使用类路径环境变量定位并装入类的常规类装入器之外,还有许多正在工作的其它类装入器。

下面的图 1 显示了 WebSphere 中正在工作的类装入器的简化图。每个椭圆代表一个类装入器,方括号中的文本描述了类装入器在何处查找类。



图 1. WebSphere 类装入器
WebSphere 类装入器

在顶部,常规 Java 系统类装入器使用类路径环境变量来装入类。第二个类装入器是特定于 WebSphere 的并且使用 ws.ext.dirs 环境变量来装入类。(有关如何更改这个环境变量的信息,请参阅“发行版说明”。在刷新中,您将能在服务器实例编辑器中更改这个变量。)除了装入所有用户代码之外,这个装入器还装入在运行时所需的所有 WebSphere 和 J2EE 类。最后,由一个或多个模块类装入器来装入运行在服务器中的模块。它们遵循前面讨论的 J2EE 类装入规则来从应用程序装入类和 JAR 文件。

上面的图 1 中最重要的是将每个类装入器定义成其上面的类装入器的子类。无论何时需要装入类,类装入器通常将请求委派给其父类装入器。如果父类装入器无法找到该类,则原始类装入器试图装入该类。请求只能在树中上行而不能下行。如果请求 WebSphere 类装入器查找 J2EE 模块中的类,它不能下行至模块类装入器来查找该类,并且将出现 ClassNotFoundException。一旦由类装入器装入一个类,则它尝试要装入的任何新类将重用同一个类装入器,或者沿着该路径上行直到找到该类为止。

两种情况下,您可能遇到问题。

  • WebSphere、J2EE 和任何全局类无法“看见”包含在应用程序中的类。如果将 JAR 文件添加到全局类路径或 ws.ext.dirs 属性中,则它就不再取决于模块中的类了。

  • 如果需要全局地将数据库驱动程序或实用程序 JAR 添加到类路径,则必须将它们添加到 ws.ext.dirs 属性而不是添加到全局类路径。如果错误地将它们添加到全局类路径,则它们将无法装入(例如)J2EE JAR 文件中的连接池类。这将再次作为 ClassNotFoundException 出现在数据库驱动程序上,但是,对数据库驱动程序类而言,向下查看 J2EE 类是错误的。

查找资源(属性文件、图像等)的公共可移植技术是使用类装入器的 findResourceXX 方法。基于前面的讨论,您可以看出为什么对这个作业使用正确的类装入器是重要的。例如,如果使用 WebSphere 类装入器,您将不能在模块中找到任何资源。





回页首


WebSphere 类装入器隔离方式

正如前面提到的,WebSphere 使用几个类装入器来从部署到服务器的模块中装入应用程序代码。这些类装入器的数量和功能取决于已在 WebSphere 服务器配置中指定的类装入器隔离方式(也称为模块可见性方式)。有四种设置可供选择:

  • Server? 对于整个服务器都使用同一个类装入器。
  • Application? 对于每个企业应用程序使用独立的类装入器。
  • Module? 对每个模块使用单独的类装入器。
  • Compatibility-- 使用与 WebSphere Application Server 3.0.x 和 3.5.x 相同的类装入器模式。这允许代码具有跨越企业应用程序边界的可见性。不赞成使用该方式,除非您正在从旧的服务器中迁移代码。




回页首


构建更大的 ? 模块相互依赖性

当在企业应用程序内构建跨越多个模块的应用程序时,请确保一个模块中的类可以看到另一个模块中它所使用的类。对于想使用企业应用程序之外的 JAR 文件的模块来说也是如此,并且对于编译时和运行时都必须完成该操作。

在编译时,更新类路径是很简单的。您只需要编辑项目的属性然后更改 Java 构建路径以包含其它项目或 JAR 文件,但且慢动手!我们将介绍一种更简便的方法,它只用一步就在编译时和运行时更新它。常规情况下,不要手工更改 J2EE 项目的 Java 构建路径。下列步骤将自动更新并维护构建路径,然后与运行时保持同步。

在运行时,当引用其它模块时,应用程序必须遵循 J2EE 规范。如上面的“WebSphere 类装入器”所述,不要简单地将一个项目放在其中一个全局类路径上,因为这可能会对类装入产生严重影响,并且使所有其它应用程序都能看见您的代码,后果同样严重。

您也不应该依赖于 WebSphere 类装入器隔离方式。如果您的服务器以(例如)Application 方式运行,则可能不必做任何更改就能使应用程序在运行时工作。不要依赖于它!以后,可能需要将应用程序部署到在 module 方式下运行的服务器中,并且您将发现应用程序不再工作。无论使用哪种隔离方式,请遵循下面建议的步骤以确保您的应用程序正确。

解决方案将利用 J2EE 模块是 JAR 文件这一事实。所有 JAR 文件可能具有一个 META-INF 文件夹,它包含 MANIFEST.MF 文件,该文件可能包含引用其它 JAR 文件的类路径。通过将一个其它模块作为一个项加入清单的类路径,当前模块可以利用其包含的类。还可以从企业应用程序中将 JAR 文件添加到清单。对这个简单解决方案的唯一限制是,其它模块决不能依赖于 Web 模块中的类,因为 Web 模块不是用位于其根处的类构建的。换句话说,模块不可能使用清单以依赖 Web 模块中的类。

Application Developer 提供了更新清单文件的简单方法。在 Navigator 或 J2EE 视图中的 J2EE 项目上单击右键,然后选择 Edit Module Dependencies。这会产生一个对话框(参阅下面的图 2),它显示了一个复选框列表,其中包含这个项目可依赖的所有其它模块和 JAR 文件。通过选择列表中的模块或 JAR 文件,可以同时更新清单和构建路径。这确保清单和构建路径总是同步的,并且提供了更改您项目相互依赖性的更简便方法。



图 2. 编辑模块相互依赖性对话框
编辑模块相互依赖性对话框




回页首


应该将 JAR 文件放在哪里?

如果仅在单个 Web 应用程序中使用 JAR 文件,则应该总是将 JAR 文件放在 Web 项目的 webApplication/WEB-INF/lib 文件夹中。这个文件夹中的 JAR 文件将自动添加到 Java 构建路径,并且当移动到另一个服务器时,将不需要任何进一步的设置。

如果 JAR 文件由同一个应用程序中的多个模块使用,则将 JAR 文件放入企业应用程序中。您将需要使用 Edit Module Dependencies 功能部件来设置清单文件和 Java 构建类路径。

如果仍然想将 JAR 文件放置在全局类路径上,(我们强烈建议您不要使用这个方法)您必须确定它是否应该放在全局类路径或 ws.ext.dirs 上。这个判定很简单。如果 JAR 文件需要访问任何 J2EE 或 WebSphere 类或者已经添加到 ws.ext.dirs 中的任何其它 JAR,还必须将它放置在 ws.ext.dirs 属性上。否则,您可以自由使用这两种属性。

最后要记住的一件事是 JAR 文件在路径中的位置越高,其包含的内容就越少。如果与 JAR 文件有牢固的相互依赖性,则更新使用该 JAR 文件的每个项目的 Java 构建路径。将它添加到全局类路径或 ws.ext.dirs 属性还意味着,必须单独地从应用程序发布 JAR 文件,并且当移动到另一个服务器时,必须再次设置服务器类路径。

使用一个全局类路径的唯一好处是次要的 ? 您可以在服务器上节省少量的磁盘空间。重大的缺点是您的应用程序现在很脆弱,其他人可能更改您所依赖的类。例如,假设您依赖某些第三方记录类,并且因为几乎所有应用程序都使用这些记录类,所以您决定在全局类路径上部署它们。使用记录类的版本 1 来测试应用程序。六个月后,部署了另一个应用程序,它需要记录类的版本 2,因此更新了日志记录 JAR。现在,您的应用程序运行在一个从来没有测试过的环境中。





回页首


类还是 JAR?

您需要作出的另一个决定是使用松散类还是使用 JAR。使用松散类的一个很大的好处是容易调试和部署它们。使用松散类,可以通过按 Ctrl-S 进行更改,如果它在可重装入的类路径上(象 WEB-INF/classes ),则可以立即使用它。如果它不在一个可重装入的类路径上,并且如果您正在使用 WebSphere Application Server,则只需要重新启动项目,然后就可使更改生效了。要完成这个任务,在 Navigator 中的项目上单击右键,然后从弹出菜单中选择 Restart project

如果您正在使用 JAR 文件,则还需要完成收集所有类和重构建 JAR 文件的其它构建步骤。您的更改不能很快生效。使用 JAR 文件的好处是使部署更清晰一点,并且更接近期望的生产环境。

如果处于项目的早期阶段,此时经常更改助手类,则您会发现使用松散类将更方便。如果类仅由单个 Web 模块使用,则将类放入 WEB-INF/classes 文件夹。为了使用类,总是使用包,因为当 Java 规范处理缺省包中的类时是不精确的。

如果类由 EJB 使用,则让您的一个 EJB 项目保留助手类(将它们放在 ejbModule 文件夹下),让需要这些助手类的其它 EJB 模块依赖于该 EJB 项目。将您的所有助手类都收集到单个 EJB 项目中,因为在项目之间不建立循环相互依赖性是 非常重要的。好的项目结构规定相互依赖性必须始终采用树的形式。一旦助手类成熟并且没有太多更改时,就切换至 JAR 方法。

如果实用程序类由企业应用程序和常规 Java 应用程序共同使用将会怎样?最简单的方法是使用这些类的 JAR。您可以用一个更复杂的替代方法:

  1. 创建一个简单的 Java 项目以存储实用程序类。
  2. 手工更新依赖于这个项目的项目 Java 构建路径。
  3. 手工更新服务器实例以包含简单 Java 项目的 bin 文件夹。




回页首


开发时类路径对比运行时类路径

在编译时,Java 编译器需要知道您的代码引用的每个类或 JAR 文件,这样就可以安全地对这些类进行编译和类型检查。Java 编译器使用项目级别属性 Java Build path 作为这个信息的唯一源。其它操作(如 Edit Module Dependencies)会操纵构建路径,但是构建路径是权威性源。

在运行时,应用程序服务器使用完全不同的机制来查找和装入类。运行时不知道任何 Java 构建路径,因此您的应用程序可能正确编译,但是在运行时仍然有 ClassNotFoundException。我们希望本文可以揭开这个过程的一些秘密。



作者简介

Tim deBoer是 IBM 多伦多实验室的 WebSphere Studio Application Developer“服务器工具”团队的一名软件开发人员。与他的团队成员一起,他目前负责 WebSphere 和 Tomcat 测试环境和 EJB 测试客户机。可以通过 deboer@ca.ibm.com与 Tim 联系。


Gary Karasiuk目前是 WebSphere Studio Application Developer 的解决方案设计师。他已经在 IBM 多伦多实验室工作了二十多年,主要从事应用程序开发工具、面向对象的编程和咨询工作。可以通过 karasiuk@ca.ibm.com与 Gary 联系。

posted @ 2007-03-20 17:50 yangrui 阅读(251) | 评论 (0)编辑 收藏

org.omg.CORBA.MARSHAL: Unable to read value from underlying bridge问题原因

在开发过程中,已经做好的程序出现了上述错误.
我的程序是用程序解析xml文件,然后用rmi返回结果.
在网上查了一下有这样的回复:

Most likely the object that the remote ejb client trying to marshall is not serializable or the object has changed meaning you might have added or deleted data member from that object. I had the later problem. Hope this helps.

然后我用以前生成的xml文件就没有出现这种错误,这说明是这个新生成的xml文件的问题.
到底问题出在哪,正在研究中.

其他的一些说法:
This problem occurs when you try to read from a EJB with a standalone client.

There at at least two causes for this:

1.) Either some class in your Bean interface is not serializable
or
2.) By using a JRE "incompatible" with WAS JRE when running your app with eclipse. ie. any other JRE than the WAS runs with...


You can correct these by
1.) checking your interface for unserializable classes, and classpath for any references for wrong version of the classes in the interface

2.) Add the WAS JRE to eclipse and use it as a default JRE
2.1) Select Window -> Preferences and JAVA -> Installed JRE's
2.2) Add {YOUR_WAS_HOME}/java/jre set it as a default
但是这种说法显然对我这种情况不适合!

posted @ 2007-03-12 13:35 yangrui 阅读(1876) | 评论 (0)编辑 收藏

Websphere环境变量设置方法

管理控制台->服务器->应用服务器->server1,进程定义->java虚拟机->类路径

posted @ 2007-02-06 15:35 yangrui 阅读(1882) | 评论 (1)编辑 收藏

Windows环境变量与重启

Windows环境变量与重启

经常看到一些软件的安装说明上写着,修改Windows的环境变量,然后重新启动计算机。这让人不禁产生疑问,修改环境变量之后真的要重启吗?
其实只要理解了环境变量的原理就可以做出正确的判断。环境变量是一些系统变量,这些变量对运行的任意一个程序都有效。最简单的确认方法就是 cmd.exe 程序。单击“开始”->“运行”,然后输入 cmd,回车,在启动的命令行窗口中输入 set,之后就可以看到一大堆各种各样的环境变量显示在屏幕上。而修改环境变量则是通过“控制面板”->“系统”->“高级”->“环境变量”对话框进行的。
这里要理解的是,一个程序启动时,环境变量被复制到该程序所在的环境中,在该程序执行过程中不会被除该程序以外的其他程序所改变。也就是说,假设我们启动了一个cmd程序,然后通过控制面板修改了环境变量设置,但是已经启动了的cmd所拥有的环境变量并不会被改变。如果我们在修改环境变量之后启动cmd程序,则该程序将拥有新的环境变量。
那么结论就很明显了:修改环境变量之后,如果受影响的是应用程序,那么只要简单地重新启动此应用程序,环境变量的修改就会反映到该程序中,而不必重新启动计算机;但是,如果受影响的是系统服务,就必须重新启动才能将环境变量的修改反映到系统服务中(因为没有办法在不重启计算机的情况下重新启动系统服务管理器)。

posted @ 2007-02-06 11:13 yangrui 阅读(3355) | 评论 (0)编辑 收藏

关于在Websphere中部署项目产生文件名过长的问题的solution

Problem:

An error such as the following can occur when you are deploying an application to WAS 6 in Windows®: "java.io.IOException: The URI length is greater than the Windows limit of 259 characters"

Cause:

The length limit on Windows is imposed by the JDK 1.4

Solution:

Shorten the temp directory used by WebSphere® Application Server (WAS), following the steps described below:
  1. Create a directory with a short name on the C drive, for example, C:\tmp.
  2. Start the WebSphere Application Server V6.0, and using the WAS Administrative Console, navigate to the servers Application Servers > server1 > Java™ and Process Management > Process Definition > Java Virtual Machine page.
  3. In the Generic JVM arguments, fill in: -Dworkspace.user.root=C:\tmp
  4. Save and re-start the WebSphere Application Server
When the server restarts, it will then use the C:\tmp directory as the WebSphere Application Server temp directory. This fixes the problem.

posted @ 2007-02-01 16:45 yangrui 阅读(328) | 评论 (0)编辑 收藏

关于读取文件内容时报sun.io.MalformedInputException的问题

在网上找到的原因是这样的:
这个问题产生的原因是IBM在JDK 1.4版本中为了追求对io通道的高效率改变了JDK 1.3中对io通道中异常处理的策略。IBM JDK 1.4中对于io通道的异常处理更加严格,因此系统中会报上面的错误。
解决这一问题的办法是不使用IBM JDK 1.4中默认的io通道,而使用nio通道。具体的做法如下:
1.打开管理控制台,找到并选择运行这个JSP的应用服务器
2.依次选择进程定义,java虚拟机,一般JVM自变量
3.添加-Dibm.stream.nio=true
4.确定,保存
5.重新启动该服务器

本来我就怀疑又是IBM JDK的问题,果然被我不幸言中了,再次表示一下郁闷!

我发现在我需要读取的文件当中存在这样的一些字符:  "怃噱珀腓镱呐涉启螓周%犴篾裱"

我原来是用下面这段程序读取这个文件的内容并打印的:
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
String str="";
String result = "";
     while((str=br.readLine())!=null){ 
           result += str + "\n";  
     }

即逐行读取的方法,我想也是一般的方法,但是就是这个readline方法导致了IBM JDK 1.4.2 报了这个sun.io.MalformedInputException的问题.

我现在使用的解决方法如下:
    String result = "";
    StringBuffer sBuffer=new StringBuffer();
             FileInputStream fis=new FileInputStream(file);
    BufferedInputStream bis=new BufferedInputStream(fis);
    String tempStr;
    byte[] b = new byte[1024];
    int i = 0;
    while ((i = bis.read(b)) > 0) {
     tempStr=new String(b,0,i);
     sBuffer.append(tempStr);
    }
    result = sBuffer.toString();

即逐字节读取的方法.这样处理就可以正常读取了!

具体到底为什么会出现这种问题现在还没弄明白,估计也弄不明白了!

posted @ 2007-01-23 13:56 yangrui 阅读(734) | 评论 (0)编辑 收藏

[原创]一个使用RMI返回java对象注意事项

在Impatience的Web Services搞得我万念俱灰的时候, 我被逼急了. 所谓"穷则思变", 我不得不考虑先放弃使用Web Services的方法返回的文件, 而是直接使用EJB RMI.
RMI需要注意的问题就是你所返回的对象必须是继承了java.io.Serializable接口. 但是我起初使用的数据格式javax.activation.DataHandler和java.io.File恰好都是不满足条件的. 其中前者直接报ClassCastException错误; 后者更加奇怪, 我在客户端可以得到一个File对象, 但是这个对象中只返回了一个服务器端这个文件的相对路径! 真的是很奇怪. 到现在我还没有找到解释.
总之, 以后在使用RMI时, 一定记住"所返回的对象必须是继承了java.io.Serializable接口"这个问题!

posted @ 2007-01-11 17:37 yangrui 阅读(462) | 评论 (0)编辑 收藏

Java远程方法调用(Remote Method Invocation, RMI)

     摘要: RMI 1.       简介 l         Java远程方法调用(Remote Method Invocation, RMI)使得运行在一个Java虚拟机(Java Virtual Machine, JVM)的对象可以调用运行另一个JVM之上的其他对象的方法,...  阅读全文

posted @ 2007-01-11 17:17 yangrui 阅读(2365) | 评论 (1)编辑 收藏

向 Web 服务传递文件(zz)

Web 服务协议已经从支持带有简单参数的非常简单的请求,发展到对现代的面向对象语言的完整支持。XML-RPC 看来是 Web 服务的早期形式之一,仅仅支持简单类型 ―― 字符串、整数、布尔值等等。SOAP 向前迈出了一步,有了用于对象的编码规则。最近的发展 ―― 在二进制数据方面的改进 ―― 来自带附件的 SOAP。

带附件的 SOAP 最初是作为 SOAP 1.1 的扩展提出的,得到了主流 SOAP 工具箱的支持。尽管 W3C 正式发布的 SOAP 1.2 还不支持附件,但是正努力实现在不远的将来(理想情况下)把它包含进来。

Web 服务与二进制数据

我毫不怀疑 XML 在应用程序集成中取得的成功,源自于对文本性编码的依赖(与二进制协议相对而言,如 CORBA ―― 一种面向对象的 RPC 协议,RMI ―― Java 专用的 RPC 标准)。优先选择文本性编码有几种原因,但最重要的可能是因为它容易调试,而且如果必要的话容易完成专门的实现。

对文本性编码的依赖仍然有不利的一面,XML 对引入二进制数据没有提供有效的解决方案。按照 W3C XML Schema 规范,二进制数据应该采用 base 64 或者十六进制编码。不幸的是,base 64 编码的数据比未编码的数据大 50%,而十六进制编码的数据是原来数据的两倍长。对于小段的二进制数据这种代价还可以接受,但对于较大的数据集显然是个问题。

二进制数据在许多应用程序中都很有用。比如:

  • 安全应用程序需要密码、散列、证书以及加密数据本身。

  • 多媒体应用程序处理图片、音乐和视频。

  • 在一些应用程序中,数据的 XML 表示被认为效率太低,比如 CAD/CAM。

  • XML 之前的成千上万种文件格式:字处理、电子表格、字体、向量图形、系谱等等。

    尽管为这些文件格式创建 XML 版本是可能的(如用于向量图形的 SVG),但二进制数据已经存在了很长时间并且可能仍然非常普及。

    最后还有 XML 自身的问题。在一个 XML 文档中包括另一个 XML 文档不是很简单的事(语法正确的解决方法依赖于 CDATA 节和字符转义)。

    为了解决这些应用程序的需要,Web 服务必须有效地支持二进制数据。提出的解决方案是带有附件的 SOAP,该协议的核心是从 XML 有效负载中去掉二进制信息将其直接作为 multipart/related MIME 内容放在 HTTP 请求中。

    在设计使用二进制数据的 Web 服务时,可以选的方法有:

  • 如果数据集很小,可以考虑在 XML 载荷中使用 base 64 编码,对于小的数据集这样做的代价不构成问题。

  • 如果数据集很大,使用附件是唯一可行的选择。

    清单 1 是一个带有 base 64 编码参数的 SOAP 请求。注意其中的 address 元素。

    清单 1. base 64 编码的参数

    POST /ws/retrieve HTTP/1.0
    Content-Type: text/xml; charset=utf-8
    Accept: application/soap+xml multipart/related, text/*
    Host: localhost:8080
    SOAPAction: ""
    Content-Length: 540
    
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
     <ps:retrieve 
          soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
          xmlns:ps="http://psol.com/2004/ws/retrieve">
      <address xsi:type="xsd:base64Binary">d3d3Lm1hcmNoYWwuY29t</address>
     </ps:retrieve>
     </soapenv:Body>
    </soapenv:Envelope>


    实现附件

    Java 开发人员可以通过 JAX-RPC(基于 XML 的 RPC 的 Java API)和 SAAJ(用于 Java 的带附件 SOAP API)使用附件。不要让缩写词 SAAJ 欺骗了您:JAX-RPC 支持附件。JAX-RPC 和 SAAJ 的区别在于抽象的层次而不是功能。

    JAX-RPC 是一种高层次的 API,比 SAAJ 更抽象。它在 RMI 层背后隐藏了大部分面向 SOAP 协议的问题。开发人员处理的是 Java 对象,预处理程序将其转成 SOAP 节点。JAX-RPC 使用 java.awt.Image 和 javax.activation.DataHandler 类表示附件。

    SAAJ 更接近于协议。使用 SAAJ 创建 SOAP 消息和 JAX-RPC 相比要做更多的工作(而且没有提供到 WSDL 的自动链接),因此多数情况您可能更愿意使用 JAX-RPC。但是为了说明附件到底是如何工作的,由于它的底层特性 SAAJ 更加合适。清单 2 是一个带有附件的 SOAP 请求。该请求要求服务器改变一个图片的大小,因为图片很大,使用附件更有效。

    清单 2. 附件参数

    POST /ws/resize HTTP/1.0
    Content-Type: multipart/related; type="text/xml"; 
       start="<EB6FC7EDE9EF4E510F641C481A9FF1F3>"; 
       boundary="----=_Part_0_7145370.1075485514903"
    Accept: application/soap+xml, multipart/related, text/*
    Host: localhost:8080
    SOAPAction: ""
    Content-Length: 1506005
    
    ------=_Part_0_7145370.1075485514903
    Content-Type: text/xml; charset=UTF-8
    Content-Transfer-Encoding: binary
    Content-Id: <EB6FC7EDE9EF4E510F641C481A9FF1F3>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
             xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
     <ps:resize 
         soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
         xmlns:ps="http://psol.com/2004/ws/resize" 
         xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
      <source href="cid:E1A97E9D40359F85CA19D1B8A7C52AA3"/>
      <percent>20</percent>
     </ps:resize>
     </soapenv:Body>
    </soapenv:Envelope>
    
    ------=_Part_0_7145370.1075485514903
    Content-Type: image/jpeg
    Content-Transfer-Encoding: binary
    Content-Id: <E1A97E9D40359F85CA19D1B8A7C52AA3>
    
    note: binary data deleted...
    
    ------=_Part_0_7145370.1075485514903--


    清单 3 示范了该 SOAP 请求的创建。该请求要求服务器改变图像的大小。过程如下:

  • 通过工厂创建 SOAP 连接和 SOAP 消息对象。

  • 从消息对象中检索消息体(中间步骤:检索 SOAP 部分和信封)。

  • 创建一个新的 XML 元素表示请求并设置编码方式。

  • 创建附件并使用 DataHandler 对象初始化。

  • 创建另外的元素表示两个参数(source 和 percent)。

  • 通过添加 href 属性把附件与第一个元素关联。附件通过 cid(content-id)URL 引用。

  • 直接把第二个参数的值设成文本并调用服务。

    服务使用改变了大小的图像(同样作为附件)作为应答。检索返回的图像之前可以测试 SOAP 错误码(表示一个错误)。如果没有错误,则作为文件检索附件并处理。

    清单 3. 使用 SAAJ

    public File resize(String endPoint,File file)
    {
      SOAPConnection connection =
       SOAPConnectionFactory.newInstance().createConnection();
      SOAPMessage message = MessageFactory.newInstance().createMessage();
      SOAPPart part = message.getSOAPPart();
      SOAPEnvelope envelope = part.getEnvelope();
      SOAPBody body = envelope.getBody();
      SOAPBodyElement operation =
       body.addBodyElement(
         envelope.createName("resize",
                   "ps",
                   "http://psol.com/2004/ws/resize"));
      operation.setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");
    
      DataHandler dh = new DataHandler(new FileDataSource(file));
      AttachmentPart attachment = message.createAttachmentPart(dh);
      SOAPElement source = operation.addChildElement("source",""),
            percent = operation.addChildElement("percent","");
      message.addAttachmentPart(attachment);
      source.addAttribute(envelope.createName("href"),
                "cid:" + attachment.getContentId());
      width.addTextNode("20");
    
      SOAPMessage result = connection.call(message,endPoint);
      part = result.getSOAPPart();
      envelope = part.getEnvelope();
      body = envelope.getBody();
      if(!body.hasFault())
      {
       Iterator iterator = result.getAttachments();
       if(iterator.hasNext())
       {
         dh = ((AttachmentPart)iterator.next()).getDataHandler();
         String fname = dh.getName();
         if(null != fname)
          return new File(fname);
       }
      }
      return null;
    }


    注意,清单 3 清楚地表明附件是在 XML 消息的 外部!为了提高效率这是必需的。

    谈到效率,看一看清单 4,这是 清单 3 更常见的 JAX-RPC 版本(也短得多)。JAX-RPC 预处理程序生成一个存根程序,极大简化了编码。您把 DataHandler 对象作为参数传递,JAX-RPC 自动生成附件。

    清单 4. 更有效的 JAX-RPC

    public File resize(File file)
      throws ServiceException, RemoteException
    {
      AttachmentService service = new AttachmentServiceLocator();
      AttachmentTip port = service.getAttachmentTip();  // get stub
      DataHandler dh = new DataHandler(new FileDataSource(file));
      DataHandler result = port.resize(dh,20);
      return new File(result.getName());
    }


    结束语

    选择是一件好事,而 SOAP 为您处理二进制数据提供了选择:您可以在 XML 有效负载中使用 base 64 编码――对于小的数据集这种方法很好,也可以向请求中附加大的没有编码的二进制文件。
  • posted @ 2007-01-09 10:42 yangrui 阅读(413) | 评论 (1)编辑 收藏

    仅列出标题
    共3页: 上一页 1 2 3 下一页