﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-kapok-随笔分类-JBOSS</title><link>http://www2.blogjava.net/kapok/category/891.html</link><description>垃圾桶,嘿嘿，我藏的这么深你们还能找到啊，真牛！</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 03:24:36 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 03:24:36 GMT</pubDate><ttl>60</ttl><item><title>轻松进行Java Portlets  开发基于JSR 168的开发和部署 </title><link>http://www.blogjava.net/kapok/archive/2005/04/22/3630.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 22 Apr 2005 13:55:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/22/3630.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3630.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/22/3630.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3630.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3630.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/webplat/200410224.html">http://dev2dev.bea.com.cn/techdoc/webplat/200410224.html</A><BR><BR>　<B>摘要</B><BR>　　Portlet是生成片段（遵守特定规范的标记语言（如HTML、XML）的片段）的Web组件。片段再合成一个完整的文档。本文介绍了关于Java Portlet的Java Specification Request (JSR) 168规范。它说明了如何使用BEA WebLogic Workshop 8.1 SP2来创建Java Portlet，以及如何将这些portlet部署到BEA WebLogic Portal 8.1 Sp2上。我将介绍一些关键概念，如门户、桌面和portlet，并详细描述多种portlet模式和窗口状态。我还将介绍如何使用Workshop来设计、实现、配置和执行portlet。<BR><BR><BR>　　JSR 168定义了有关Java Portlet的规范。门户是一个Web应用程序和一个portlet的聚合。Portlet容器运行portlet，并管理它们的生命周期。JSR 168定义了portlet与portlet容器之间的契约，它没有定义portlet容器与门户之间的契约。门户的实现留给了门户供应商。<BR><BR>　　<B>BEA WebLogic门户</B><BR>　　BEA WebLogic Portal (8.1 SP2)的当前版本支持不同类型的portlet：JSP/HTML portlet、Java PageFlow portlet、Struts portlet和Java portlet，将来还会支持其他portlet，如Web Services for Remote Portlets (WSRP)。我们将着重介绍Java portlet。<BR><BR>　　WebLogic Portal提供了JSR 168中未描述的门户功能，包括但不限于：书和页面中portlet的组织、多渠道支持和使用skin、skeleton和shell定制。<BR><BR>　　为了能够继续下去，在进行下一部分之前，请先完成以下内容：<BR>　　　·使用WebLogic Domain Configuration Wizard创建一个门户域（如JSR168PortalDomain）。 <BR>　　　·使用WebLogic Workshop创建一个使用上面所建立域的门户应用程序（如JSR168PortalApp）。 <BR>　　　·在门户应用程序内创建一个门户Web项目（如JSR168PortalWebProject）。 <BR>　　　·在门户Web项目中创建一个WebLogic Portal .portal文件（如JSR168.portal）。 <BR>　　　·启动服务器实例。 <BR><BR>　　<B>创建您的第一个Java Portlet</B><BR>　　下面的步骤描述了如何创建您的第一个JSR 168 portlet。<BR>　　·在门户Web项目（如JSR168PortalWebProject）中，使用WebLogic Workshop为portlet（入FirstPortlet）创建一个新文件夹。 <BR>　　·在新文件夹内使用Wizard通过创建相应的.portlet文件创建一个新portlet（如Firstportlet）。<BR>　　·选择portlet类型为Java Portlet。 <BR>　　·指定标题（如First）。 <BR>　　·指定定义标签（如first）。<BR>　　·指定类名称（如com.malani.examples.portlets.jsr168.FirstPortlet）。 <BR>　　·打开门户（如JSR168.portal）。 <BR>　　·将portlet（如Firstportlet）拖放到门户中的页面上（如JSR168.portal）。 <BR>　　·运行.portal文件进行测试。 <BR><BR>　　您的第一个JSR 168 portlet已经成功运行了！但向导在背后作了些什么呢？<BR>　　·它创建了一个特定于WebLogic Workshop和WebLogic Portal的.portlet文件。.portlet文件构成了与特定于Workshop和WebLogic Portal的.portal文件的契约。 <BR>　　·向导创建了一个.java文件（如com.malani.examples.portlets.jsr168.FirstPortlet.java），该文件放置在WEB-INF/src目录中。<BR>　　·向导创建了一个WEB-INF/portlet.xml配置文件，并为portlet在文件中插入了一个条目。该portlet的条目看上去如下：<BR><BR><FONT color=#666666>&lt;portlet&gt;<BR>&lt;description&gt;Description goes here&lt;/description&gt;<BR>&lt;portlet-name&gt;first&lt;/portlet-name&gt;<BR>&lt;portlet-class&gt;com.malani.examples.portlets.jsr168.FirstPortlet<BR>&lt;/portlet-class&gt;<BR>&lt;portlet-info&gt;<BR>&lt;title&gt;First&lt;/title&gt;<BR>&lt;/portlet-info&gt;<BR>&lt;/portlet&gt; </FONT><BR><BR><B>　　Java Portlet类</B><BR>　　在该示例中，向导生成的Portlet Java文件扩展了javax.portlet.GenericPortlet类。GenericPortlet类实现了javax.portlet.Portlet接口。图1是一个Unified Modeling Language (UML)类图，描述了这些关系。通过直接实现portlet接口，可以编写一个portlet。然而，GenericPortlet是一个创建portlet的更方便方法。首先，我们看一下portlet生命周期、portlet模式和window状态。 
<P align=center><IMG height=287 src="http://dev2dev.bea.com.cn/techdoc/wlintegra/images/image2004102206.gif" width=460><BR>图 1</P>
<P>　　<B>Portlet生命周期</B><BR>　　为了成功地创建portlet，您必须遵照portlet生命周期。javax.portlet.Portlet接口中的方法定义了该生命周期，这些生命周期方法是init()、render()、processAction()和destroy()。当部署portlet的实例时调用init()方法。它用于获得所需的任何昂贵资源（如后台连接），并执行其他一次性活动。当portlet的实例被撤销部署时，使用destroy()方法来释放这些资源。<BR><BR>　　Portlet规范清晰区别render请求和动作请求。图2描述了portlet请求和响应的一个UML类图。门户页面上的render请求会导致对所页面上的每个portlet上调用render()方法，当用户在特定portlet上调用某个动作（通常是HTML表单提交）时，将会调用该portlet的processAction()方法。这样，用户的动作请求转换为processAction()方法的一次调用和render()方法的多次调用。</P>
<P align=center><IMG height=231 src="http://dev2dev.bea.com.cn/techdoc/wlintegra/images/image2004102207.gif" width=459><BR>图 2</P>
<P>　　图3是一个序列图，说明了调用processAction()方法的效果，以及为同一页面上的portlet进行后续render()方法的调用。关于更多信息，请参阅关于处理动作的一节。</P>
<P align=center><IMG height=293 src="http://dev2dev.bea.com.cn/techdoc/wlintegra/images/image2004102208.gif" width=462><BR>图 3</P>
<P>　　有两种重载的init()方法，一个没有参数，另一个有一个javax.portlet.PortletConfig类的实例。注意：关于init(PortletConfig)有一个特殊的caveat。调用super.init(aPortletConfig)失败将导致一个NullPointerException。所包含的源代码示例中的Init portlet说明了这种行为（源代码可以在www.sys-con.com/weblogic/source.cfm中找到）。<BR><BR><B>　　Portlet模式</B><BR>　　JSR 168定义了三种Portlet模式：VIEW、EDIT和HELP。一个portlet实例在任何时候都可以恰巧在一种　portlet模式下。其他自定义portlet模式（如配置和源）都是可能的。VIEW模式是默认的模式。Portlet规范建议EDIT模式允许portlet用户定制portlet实例，以及HELP模式显示关于portlet的用法信息。Portlet必须支持VIEW模式，但在portlet中对EDIT模式和HELP模式的支持是可选的。例如，portlet First portlet示例不支持EDIT模式和HELP模式。<BR><BR><B>　　window状态</B><BR>　　JSR 168定义了三种Window状态：NORMAL、MINIMIZED和MAXIMIZED。Portlet实例任何时候都可以恰好是一种window状态。其他自定义window状态（如半页）也是可能的。在NORMAL状态下，portlet占了屏幕区的一小部分。屏幕状态与其他portlet共享。在MINIMIZED状态下，portlet的内容被隐藏。在MAXIMIZED状态下，portlet的内容占屏幕区的大部分。其他共享同一页面的portlet在MAXIMIZED状态下被隐藏。例如，portlet First示例支持所有三种window状态。<BR><BR><B>　　GenericPortlet类</B><BR>　　您创建的大多数portlet将会扩展javax.portlet.GenericPortlet类，而不是直接实现javax.portlet.Portlet接口。GenericPortlet类实现了render()方法。如果portlet的window状态被最小化，那么render()方法不能做任何事情。如果portlet的window状态不是最小化，那么render()方法设置在portlet.xml文件中指定的标题，并调用doDispatch()方法。根据Portlet模式， doDispatch()方法适当地调用doView()、doEdit()和doHelp()方法。这样，由于GenericPortlet类帮助实现render()方法，并且提供doView()、doEdit()和doHelp()方法来覆盖，因此GenericPortlet类比Portlet接口更便于扩展。 <BR><BR>　　考虑一下First portlet示例。FirstPortlet类扩展了GenericPortlet，FirstPortlet改写了doView()方法。<BR><BR><FONT color=#666666>public void doView(RenderRequest request, RenderResponse response)<BR>throws PortletException, IOException<BR>{<BR>response.setContentType("text/html");<BR>response.getWriter().write("&lt;p&gt;Hello World&lt;/p&gt;");<BR>}</FONT><BR><BR>　　注意：调用setContentType()方法前调用getWriter()方法会导致java.lang.IllegalStateException。<BR><BR><B>　　实现Portlet模式</B><BR>　　VIEW模式是强制的，但EDIT和HELP模式是可选的。为了实现EDIT和HELP portlet模式，需要在portlet类中实现适当的doEdit()和doHelp()方法。请参考包含在源代码示例（本文的源代码可以在www.sys-con.com/wldj/sourcec.cfm找到）中的portlet Mode。此外，必须在portlet.xml中如下配置各模式：<BR><BR><FONT color=#666666>&lt;supports&gt;<BR>&lt;mime-type&gt;text/html&lt;/mime-type&gt;<BR>&lt;portlet-mode&gt;edit&lt;/portlet-mode&gt;<BR>&lt;portlet-mode&gt;help&lt;/portlet-mode&gt;<BR>&lt;/supports&gt; </FONT><BR><BR>　　注意：修改portlet.xml配置文件，但不实现portlet类中的相应方法，会导致javax.portlet.PortletException。<BR><BR><B>　　实现window状态</B><BR>　　JSR 168没有描述禁用window状态支持的方法。然而，WebLogic Portal实现了对它们的禁用。为了禁用portlet对window状态的支持，需要在weblogic-portlet.xml文件中排除window状态：<BR><BR><FONT color=#666666>&lt;portlet&gt;<BR>&lt;portlet-name&gt;state&lt;/portlet-name&gt;<BR>&lt;supports&gt;<BR>&lt;mime-type&gt;text/html&lt;/mime-type&gt;<BR>&lt;excluded-window-state&gt;minimized&lt;/excluded-window-state&gt;<BR>&lt;excluded-window-state&gt;maximized&lt;/excluded-window-state&gt;<BR>&lt;/supports&gt;<BR>&lt;/portlet&gt; </FONT><BR><BR>　　请参考源代码示例中的portlet State。<BR><BR>　　<B>包含JavaServer Pages (JSPs)</B><BR>　　考虑portlet First的doView()方法，该方法获得了Writer的实例，并直接输出HTML片段。由于多种原因（如为了达到Java逻辑与HTML视图表现的分离），往往不推荐输出直接的HTML片段。推荐的方法是使用JSP来显示视图。portlet类中的方法执行业务逻辑、设置render参数以及包含JSP。为了包含一个特定的JSP，应首先获得PortletContext。从PortletContext实例中，通过调用getRequestDispatcher()方法获得一个PortletRequestDispatcher的实例。通过调用include()方法来包含JSP。例如：<BR><BR><FONT color=#666666>// execute the necessary logic here...<BR>PortletRequestDispatcher aDispatcher =<BR>getPortletContext().getRequestDispatcher(<BR>"/IncludePortlet/includeView.jsp"<BR>);<BR>aDispatcher.include(aRequest, aResponse); </FONT><BR><BR>　　注意：在执行render()方法时，portlet可能只使用一个PortletRequestDispatcher对象。<BR>　　请参考包含在源代码中的portlet Include。JSP页面（如includeView.jsp）不包含根HTML标签（如&lt;html&gt;、&lt;title&gt;和&lt;body&gt;），因为这些标签由门户框架提供。JSP页面只包含显示portlet所必需的HTML片段。<BR><BR>　　<B>处理动作</B><BR>　　在一个标准的Web应用程序中，一个HTML表单提交将导致执行一些业务逻辑。业务处理的结果，要么作为属性而被设置在请求或会话中并转发，要么包含到下一个JSP。<BR><BR>　　在一个JSR 168 portlet中，一个HTML表单的动作URL应该是什么样呢？JSR 168定义了一个JSP标签库，称为portlet taglib。HTML表单的动作URL可以使用actionURL portlet标签生成。例如（请参考favoriteColorEdit.jsp文件）：<BR><BR><FONT color=#666666>&lt;form action="&lt;portlet:actionURL/&gt;" method="post"&gt;<BR>...<BR>&lt;/form&gt; </FONT><BR><BR>　　提交该HTML表单将会导致调用portlet的processAction(ActionRequest aRequest, ActionResponse aResponse)方法。像通常一样，可以通过调用request对象的getParameter()方法来获得表单参数。注意：通过提交表单调用动作，但portlet中却没有processAction()方法，将会导致javax.portlet.PortletException。<BR><BR>　　processAction()方法设置response对象中的值。不要使用ActionRequest或ActionResponse对象的setAttribute()方法。值不会从processAction()传递到render()方法，而且在JSP中是不可用的。相反要使用ActionResponse对象的setRenderParameter()方法。这些render参数将对所有后续render请求可用，这一点与典型的Web应用程序请求属性很不相同。典型的Web application请求属性只对于一个请求可用。另一方面，render请求参数对于许多后续render请求可用。render参数保持可用直到值被动作的重新执行显式地修改或删除。<BR><BR>　　考虑portlet FavoriteColor。它在VIEW模式显示了一个用户偏好的颜色，但是可以在EDIT模式下更改。在EDIT模式下提交偏好的颜色选择将调用processAction()方法。该方法获得偏好的颜色请求参数，并将其设置为render参数。这样，偏好的的颜色render参数将在所有后续render请求中都可用。<BR><BR>　　所呈现的参数是怎样显示在JSP上的呢？应使用来自portlet标签库的defineObjects标签来定义portlet对象。该标签使renderRequest、renderResponse和portletConfig portlet对象在页面中可用。参数通过调用renderRequest对象的getParameter()方法来显示。请参考与所包含的源代码示例中的favoriteColorView.jsp。<BR><BR>　　portlet FavoriteColor也展示了其他概念。第一个是如何在processAction()方法中用编程的方法改变portlet模式。调用ActionResponse对象的setPortletMode()方法来修改portlet模式。第二个概念是如何使用一个HTML链接来修改portlet模式。该链接使用来自portlet标签库的renderURL标签生成。根据希望的portlet模式指定portletMode属性的值。请参考源代码示例中的FavoriteColorPortlet类和favoriteColorView.jsp页面。 <BR><BR><B>　　Portlet Preferences</B><BR>　　Portlet Preferences（Portlet首选项）是portlet的基本配置数据。一个preference是一个“名称和值”对。名称的类型是一个字符串，而值的类型是字符串或字符串数组。Portlet Preference不适于存储任意数据。portlet容器为portlet preferences提供持久性。在WebLogic Portal中，preference的持久性只在下面两个条件都为真时才起作用： <BR>　　·门户运行在桌面中，而不是DOT门户模式。<BR>　　·用户已经登录。<BR><BR>　　<B>桌面与DOT门户模式</B><BR>　　在WebLogic Workshop中创建.portal文件时，像书、页面和portlet等项都可以被拖放到.portal文件中，.portal文件能够直接从Workshop内运行。然而，某些功能，如preferences的存储，在这种DOT门户模式下运行时是不可用的（DOT门户模式也称为单文件模式（Single File Mode））。 <BR><BR>　　其他模式称为桌面模式。创建一个门户时使用Portal Administrator。在门户内，一个桌面被创建。像图书、页面和portlet等项被创建，并放置在桌面中。在这种模式下，某些功能，像preferences的存储，是可用的（桌面模式也被称为流模式（Streamed Mode））。<BR><BR>在继续讨论前，先创建一个桌面：<BR>　　启动Portal Administration（譬如，<A href="http://localhost:7001/JSR168PortalAppAdmin/" target=_blank>http://localhost:7001/JSR168PortalAppAdmin/</A>）。一种启动Portal Administration的方法是直接从Workshop中启动。选择Portal菜单，选中Portal Administration菜单项。 <BR>　　·登录进Portal Administration。<BR>　　·创建一个新门户（譬如，JSR168）。 <BR>　　·在门户中，创建一个新桌面（如d1）。 <BR>　　·将LoginPortlet添加到桌面的一个页面中。 <BR>　　·将ContactPortlet添加到桌面的一个文件中。 <BR><BR><B>　　Portlet Preferences示例</B><BR>　　Contact portlet演示了Portlet Preferences。Portlet Preferences可以是静态的或动态的。静态 preferences与portlet一起在portal.xml文件中指定。例如，ContactPortlet具有一个成为contact-preference的 preferences。contact-preference的默认值也被指定：<BR><BR><FONT color=#666666>&lt;portlet-preferences&gt;<BR>&lt;preference&gt;<BR>&lt;name&gt;contact-preference&lt;/name&gt;<BR>&lt;value&gt;Email&lt;/value&gt;<BR>&lt;/preference&gt;<BR>&lt;/portlet-preferences&gt; </FONT><BR><BR>　　动态 preferences不在portlet.xml配置文件中预定义。当portlet运行时，这些preferences被存储和读取。在运行期间，一个javax.portlet.PortletPreferences接口的实例包含这些preferences。该实例通过调用PortletRequest对象的getPreferences()方法获得。特定preferences的值通过调用preferences实例上的getValue()方法来获得。 <BR><BR>　　调用preferences实例的setValue()方法会更新一个preferences值。然而，需要一个额外的步骤来提交这些修改。preferences实例的store()方法被调用来使preferences持久化。preferences只能在processAction()方法中进行修改。如果在processAction()方法中没有调用store()方法，任何对preferences实例的修改都会被丢弃。注意：就如前面提到的，如果用户没有登录或门户处于DOT门户模式，那么调用store()方法将会导致一个运行时异常。<BR><BR>　　在portlet和servlet之间有很多相似点。然而，它们也存在着重要区别。portlet规范建立在servlet规范之上。portlet容器存在于servlet容器中。就像servlet部署在一个Web应用程序中，portlet也是如此。Servlet和Web应用程序使用portlet.xml文件进行配置。一个servlet具有显式的生命周期：init()、doGet()、doPost()等。类似地，一个portlet也具有显式的生命周期：doView()、doEdit()、processAction()等。servlet和portlet类的方法必须以安全线程的方式编码。<BR><BR>　　然而，也存在着重要的区别。Servlet被允许进行include、forward和redirect操作；然而portlet只被允许进行include操作。Servlet能够呈现一个完整的页面，但portlet只提交页面片段。portlet具有严格定义的portlet模式和Window状态，这方面不像servlet。Portlet具有更正式的请求，对render请求和动作请求进行处理，它们也具有preferences。portlet并不是servlet！<BR><BR><B>　　结束语</B><BR>　　本文通过使用一个简单的向导描述portlet的创建而开始，并说明了portlet的生命周期以及portlet类实现的内部工作方式，描述了portlet.xml配置文件和相应的weblogic-portlet.xml配置文件的结构和语义。对各种概念，如portlet模式和window状态，本文也进行了解释。本文演示了portlet标签库的用法和portlet中的表单处理。最后，我介绍了如何使用portletpreferences。理解了本文所介绍的这些知识和概念，您就可以在创建和部署自己的强大portlet的道路上前进了。<BR><BR><B>　　致谢</B><BR>　　感谢Subbu Allamaraju、Max Cooper、Steve Ditlinger、David Lu、Roshni Malani和Alex Toussaint，他们仔细阅读了这篇文章，并提供了有价值的反馈意见。<BR><BR><B>　　参考资料</B><BR>　　· 要讨论这篇文章、并提问问题，从这里开始： <A href="http://www.bartssandbox.com/" target=_blank>www.bartssandbox.com</A>。需要免费成员资格。 <BR>　　· 下载、阅读JSR 168：<A href="http://www.jcp.org/en/jsr/detail?id=168" target=_blank>www.jcp.org/en/jsr/detail?id=168</A> <BR>　　· WebLogic Portal文档的起始点：<A href="http://e-docs.bea.com/wlp/docs81/index.html" target=_blank>e-docs.bea.com/wlp/docs81/index.html</A><BR>　　· 建立Workshop Help的Java Portlet部分：<A href="http://e-docs.bea.com/workshop/docs81/doc/en/core/index.html" target=_blank>e-docs.bea.com/workshop/docs81/doc/en/core/index.html</A> <BR>　　· 用WebLogic Portal 8.1开发JSR 168 Portlet：<A href="http://dev2dev.bea.com/products/wlportal81/articles/JSR168.jsp%20" target=_blank>dev2dev.bea.com/products/wlportal81/articles/JSR168.jsp </A><BR>　　· Web Services for Remote Portlets (WSRP)规范：<A href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp" target=_blank>www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp</A> <BR>　　· 尝试一下WSRP：<A href="http://dev2dev.bea.com/codelibrary/code/wsrp_supportkit.jsp" target=_blank>dev2dev.bea.com/codelibrary/code/wsrp_supportkit.jsp</A> <BR>　　· Single File Mode和Streamed Rendering Mode：<A href="http://developers.sun.com/prodtech/portalserver/reference/techart/jsr168/pb_whitepaper.pdf" target=_blank>单击这里</A>！<BR>　　· 有关Portlet规范上的文章：<BR>- 介绍Portlet规范，第1部分：<BR><A href="http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-portlet_p.html" target=_blank>www.javaworld.com/javaworld/jw-08-2003/jw-0801-portlet_p.html</A><BR>-介绍Portlet规范，第2部分：<BR><A href="http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-portlet2_p.html" target=_blank>www.javaworld.com/javaworld/jw-09-2003/jw-0905-portlet2_p.html</A><BR>　　· 对JSR 168白皮书的介绍：单击这里！<BR>　　· Java Passion Portlet演讲笔记：<A href="http://www.javapassion.com/j2eeadvanced/Portlet4.pdf" target=_blank>www.javapassion.com/j2eeadvanced/Portlet4.pdf</A> <BR><BR><B>　　关于作者</B><BR>　　Prakash Malani在架构、设计和开发面向对象的软件方面具有广泛的经验，曾经在很多应用领域从事过软件开发，如娱乐、零售、机械、通信和互动电视等。他实践和指导着很多领先的技术，如J2EE、UML和XML。　　　Prakash已经在多个行业领先的出版物上发表了多篇文章。（<A href="http://www.sys-con.com/author/?id=3573" target=_blank>更多内容</A>）<BR><BR><B>　　源代码</B><BR>　　<A href="http://photos.sys-con.com/story/res/45565/Malani0306.zip" target=_blank>源代码-Zip文件</A> <BR>　　英文原文：<A href="http://www.sys-con.com/story/?storyid=45565&amp;DE=1" target=_blank>http://www.sys-con.com/story/?storyid=45565&amp;DE=1</A><BR></P><img src ="http://www.blogjava.net/kapok/aggbug/3630.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-22 21:55 <a href="http://www.blogjava.net/kapok/archive/2005/04/22/3630.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>灰狐上面关于JBoss的连接</title><link>http://www.blogjava.net/kapok/archive/2005/03/30/2615.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 30 Mar 2005 09:28:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/30/2615.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/2615.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/30/2615.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/2615.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/2615.html</trackback:ping><description><![CDATA[<A href="http://www.huihoo.org/jboss/">http://www.huihoo.org/jboss/</A><img src ="http://www.blogjava.net/kapok/aggbug/2615.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-30 17:28 <a href="http://www.blogjava.net/kapok/archive/2005/03/30/2615.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JBoss3.0 下配置和部署EJB简介</title><link>http://www.blogjava.net/kapok/archive/2005/03/30/2614.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 30 Mar 2005 09:19:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/30/2614.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/2614.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/30/2614.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/2614.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/2614.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: (by huihoo.org 赵晨希,zhaochenxi@vip.sina.com)http://www.huihoo.org/jboss/jboss3_ejb.htm1．JBoss简介JBoss是一个运行EJB的J2EE应用服务器。它是开放源代码的项目，遵循最新的J2EE规范。从JBoss项目开始至今，它已经从一个EJB容器发展成为一个基于的J2EE的一个web 操作系统（oper...&nbsp;&nbsp;<a href='http://www.blogjava.net/kapok/archive/2005/03/30/2614.html'>阅读全文</a><img src ="http://www.blogjava.net/kapok/aggbug/2614.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-30 17:19 <a href="http://www.blogjava.net/kapok/archive/2005/03/30/2614.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Jboss 源码解析</title><link>http://www.blogjava.net/kapok/archive/2005/03/29/2569.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 29 Mar 2005 01:46:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/29/2569.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/2569.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/29/2569.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/2569.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/2569.html</trackback:ping><description><![CDATA[<A href="http://blog.csdn.net/stripbolt/archive/2004/10/24/149705.aspx">http://blog.csdn.net/stripbolt/archive/2004/10/24/149705.aspx</A><BR><BR>JBoss是一个非常优秀的J2EE的Application Server,研究 <BR>它的源代码有助于我们更好的理解J2EE的各种技术。 <BR>本系列拟从四个方面分析Jboss源码： <BR>1.EJB Container实现 <BR>2.Transaction实现 <BR>3.Persistence Mapping <BR>4.Client到Server端的Invocation<BR><BR>-------------------------------------------------------------------<BR>先说第1点:EJB Container实现。 <BR>1.1 EJB Pool <BR>我们知道，EJB Container 会维护一个EJB池， <BR>在多个client间共享，避免频繁创建销毁对象的开销。 <BR>让我们来看看Jboss实现的Pool: <BR>EJB分EntityBean,MDB,Stateless/Stateful Session Bean, <BR>而Jboss中也对应的有EntityInstancePool，MessageDrivenInstancePool, <BR>StatefulSessionInstancePool,StatelessSessionInstancePool. <BR>让我们先从这4个类的共同基类AbstractInstancePool看起： <BR>class AbstractInstancePool实现了接口InstancePool，该接口有以下几个方法： <BR>EnterpriseContext get() throws Exception; <BR>void free(EnterpriseContext ctx); <BR>void discard(EnterpriseContext ctx); <BR>int getCurrentSize(); <BR>public int getMaxSize(); <BR>--------------------------------------------------------------------<BR>先对EnterpriseContext作番说明。EnterpriseContext的作用 <BR>就是把具体的EJB instance和它的metadata联系起来。 <BR>该类签名为： <BR>public abstract class EnterpriseContext， <BR>有4个子类，EntityEnterpriseContext，MessageDrivenEnterpriseContext， <BR>StatefulSessionEnterpriseContext，StatelessSessionEnterpriseContext。 <BR>分别对应4种类型的EJB。 <BR>EnterpriseContext中有几个重要的成员变量。 <BR>/** The EJB instance */ <BR>Object instance; <BR>/** The container using this context */ <BR>Container con; //Container这个类是JBoss用来代表对EJB提供Transaction,Security，Pool等服务的类，我们回头还会再说。 <BR>/** Only StatelessSession beans have no Id, stateful and entity do */ <BR>Object id; <BR><BR>/** The transaction associated with the instance */ <BR>Transaction transaction; //Transaction，我们下节再说. <BR><BR>// Constructors -------------------------------------------------- <BR><BR>public EnterpriseContext(Object instance, Container con) <BR>{ <BR>this.instance = instance; <BR>this.con = con; <BR>} <BR><BR>public Object getInstance() <BR>{ <BR>return instance; <BR>} <BR>public Container getContainer() { <BR>return con; <BR>} <BR><BR>public void setId(Object id) { <BR>this.id = id; <BR>} <BR><BR>public Object getId() { <BR>return id; <BR>} <BR><BR>public void setTransaction(Transaction transaction) { <BR>this.transaction = transaction; <BR>} <BR><BR>public Transaction getTransaction() { <BR>return transaction; <BR>} <BR><BR>/** <BR>* Get the EJBContext object <BR>*/ <BR>public abstract EJBContext getEJBContext(); //由子类实现 <BR><BR>//返回javax.ejb.EJBContext，注意这个EJBContext是 <BR>EJB规范要求提供给EJB的Context,与JBoss自己实现类EnterpriseContext没有关系。 <BR><BR>/** The instance is being used. This locks it\'s state */ <BR>int locked = 0; <BR>public void lock() <BR>{ <BR>locked ++; <BR>} <BR><BR>public void unlock() { <BR>locked --; <BR>} <BR><BR>public boolean isLocked() { <BR>return locked != 0; <BR>} <BR>//lock这个成员变量表示当前这个EJB instance有没人在用。 <BR>//这个变量用来给Reentrance,以及canPassviate用. <BR>/** <BR>* before reusing this context we clear it of previous state called <BR>* by pool.free() <BR>* 从pool里取出来的时候没有关联任何EJB instance和Transaction信息 <BR>* 在返还pool的时候把这些信息清掉。 <BR>*/ <BR>public void clear() { <BR>this.id = null; <BR>this.locked = 0; <BR>this.transaction = null; <BR>} <BR>//------------------------------------------------------------------------------------- <BR>protected boolean isContainerManagedTx() <BR>{ <BR>BeanMetaData md = (BeanMetaData)con.getBeanMetaData(); <BR>return md.isContainerManagedTx(); <BR>} <BR>//从关联的container拿出对应的metadata,判断是否CMT. <BR>//注意这里con是Container成员变量，可不是什么连接，连接一般缩写为conn, <BR>//我一开始就搞混了:) <BR>//BeanMetaData这些MetaData的子类都是从xml配置里头读出来的Metadata构造的, <BR>//没什么神秘的. <BR>这个类里头还有两个inner class,EJBContextImpl implements EJBContext, <BR>UserTransactionImpl implements UserTransaction。EJBContextImpl等 <BR>讲解过Container再说，UserTransaction等下节再说。 <BR><BR>现在让我们来看看EnterpriseContext的几个子类： <BR>首先类比一下几个子类的成员变量： <BR>EntityEnterpriseContext： <BR>private EJBObject ejbObject; <BR>private EJBLocalObject ejbLocalObject; <BR>private EntityContext ctx; <BR><BR>StatefulSessionEnterpriseContext <BR>private EJBObject ejbObject; <BR>private EJBLocalObject ejbLocalObject; <BR>private SessionContext ctx; <BR><BR>StatelessSessionEnterpriseContext <BR>EJBObject ejbObject; <BR>EJBLocalObject ejbLocalObject; <BR>SessionContext ctx; <BR><BR>MessageDrivenEnterpriseContext： <BR>private MessageDrivenContext ctx; <BR>看来除了MDB没有对应的EJBObject/EJBLocalObject,其他统统都有：） <BR>学过EJB的人都知道，在语法上EJB instance 是implements <BR>EntityBean/SessionBean/MessageDrivenBean (这3个interface有一个共同的 <BR>interface: public interface EnterpriseBean extends Serializable, <BR>EnterpriseBean 是个Marker接口,里头什么都没有，好像Serializable接口一样，只说明 <BR>实现它的是个EJB.而EntityBean/SessionBean/MessageDrivenBean 有对应的 <BR>void ejbActivate() throws EJBException, RemoteException; <BR>void ejbLoad() throws EJBException, RemoteException; <BR>一些方法需要实现。（虽然常常是空实现：），由容器作了很多事) <BR>至于EJBObject/EJBLocalObject 在语法上都是非直接实现的，代表EJB instance暴露出的Remote/Local 接口。既然这些EJB instance非直接实现这些接口，那么这些接口如何与具体的EJB instance相关联，我们讲到Container类时就知道了。 <BR>---------------------------------------------------------------------------------- <BR><BR><BR>EnterpriseContext的子类EntityEnterpriseContext的构造函数<BR>public EntityEnterpriseContext(Object instance, Container con) <BR>throws RemoteException <BR>{ <BR>super(instance, con); <BR>ctx = new EntityContextImpl(); //这是EJBContextImpl的子类，不急着说:) <BR>((EntityBean)instance).setEntityContext(ctx);//看看，原来set...Context是在这里调用的//其它instance实现的什么ejbCreate(),EJBLoad,EJBActive()到底是什么时候 <BR>调用的呢？接下来我们慢慢找，这可散布在各地呢：） <BR>} <BR>//其他几个子类的构造函数前3行也都如此，是类似的。 <BR>对于StatelessSessionEnterpriseContext和MessageDrivenEnterpriseContext,紧接的是 <BR><BR>Method ejbCreate = instance.getClass().getMethod(\"ejbCreate\", new Class[0]); <BR>ejbCreate.invoke(instance, new Object[0]); <BR>//看看，来了一个ejbCreate:),对于StatelessSessionBean，一上来就ejbCreate()了~ <BR>接下来看点简单的：对于EnterpriseContext, <BR>有public abstract void discard() throws RemoteException; <BR>对于MessageDriven...的 <BR>public void discard() throws RemoteException <BR>{ <BR>((MessageDrivenBean)instance).ejbRemove(); <BR>} <BR>对于StatelessSession的 <BR>public void discard() throws RemoteException <BR>{ <BR>((SessionBean)instance).ejbRemove(); <BR>} <BR>对于StatefulSession...的 <BR>public void discard() throws RemoteException <BR>{ <BR>// Do nothing <BR>} <BR>对于Entity.... <BR>public void discard() throws RemoteException <BR>{ <BR>((EntityBean)instance).unsetEntityContext(); <BR>} <BR>//discard 是在AbstractInstancePool中的 <BR>void discard(EnterpriseContext ctx);调用的 <BR><BR>/** <BR>* Discard an anonymous instance after invocation. <BR>* This is called if the instance should not be reused, perhaps due to some <BR>* exception being thrown from it. <BR>* <BR>* @param ctx The context to discard. <BR>*/ <BR>public void discard(EnterpriseContext ctx) <BR>{ <BR>ctx.discard(); <BR>} <BR>AbstractInstancePool还有个free方法： <BR>public void free(EnterpriseContext ctx) { <BR>ctx.clear();//记得ctx只是清掉id,transaction等，返回pool,准备复用么？ <BR>//discard是在发生错误的时候，不希望扩大影响，于是discard掉， <BR>//而对应get()的free方法只是把EnterpriseContext返还给pool. <BR>} <BR>---------------------------------------------------------------------------------- <BR><BR>专门对于Stateful....Context有设置EJB instance的函数 <BR>/** <BR>* During activation of stateful session beans we replace the instance <BR>* by the one read from the file. <BR>*/ <BR>public void setInstance(Object instance) <BR>{ <BR>this.instance = instance; <BR>((SessionBean)instance).setSessionContext(ctx); <BR>} <BR>注意在4种...Context中，只有Stateful...Context签名 <BR>public class StatefulSessionEnterpriseContext <BR>extends EnterpriseContext <BR>implements Serializable <BR>多了个implements Serializable,(其他都没有), <BR>这是为了支持Activation/Passviation,Passviation的时候 <BR>把EJB Instance写到文件中。 <BR>在StatefulSessionEnterpriseContext中有这样两个方法 <BR>private void writeObject(ObjectOutputStream out) <BR>throws IOException, ClassNotFoundException <BR>{ <BR>// do nothing <BR>} <BR><BR>private void readObject(ObjectInputStream in) <BR>throws IOException, ClassNotFoundException <BR>{ <BR>// do nothing <BR>} <BR>//说明序列化的时候StatefulSessionEnterpriseContext本身不需要 <BR>写出什么状态，只有对应的EJB instance才需要写出状态。<BR>
<DIV>现在来看AbstractInstancePool的源码，由于内容太多 <BR>就不详细解释了。 <BR><BR>先看Abstract...Pool的create(Object instance)函数热热身~ <BR>Abstract...Pool <BR>// Protected ----------------------------------------------------- <BR>protected abstract EnterpriseContext create(Object instance) <BR>throws Exception; <BR>对Entity...Pool <BR>protected EnterpriseContext create(Object instance) <BR>throws Exception <BR>{ <BR>return new EntityEnterpriseContext(instance, getContainer()); <BR>} <BR>其他3种Pool类似。 <BR>再回头看Abstract..Pool <BR>/** The pool data structure */ <BR>protected EnterpriseContext[] pool; <BR>protected int currentIndex = -1; <BR>/** The maximum number of instances allowed in the pool */ <BR>protected int maxSize = 30; <BR>/** The minimum size of the pool */ <BR>protected int minSize = 0; <BR>/** determine if we reuse EnterpriseContext objects i.e. if we actually do pooling */ <BR>protected boolean reclaim = false;//对于MDB和StatelessSessionBean,这个变量被设为true,// for MDB, we *do* pool // for SLSB, we *do* pool <BR><BR>ok,现在来看pool的重头戏，get()/free(EnterpriseContext ctx)函数 <BR>（discard(EnterpriseContext ctx)前面讲过，不再重复) <BR>这几个函数也是Interface InstancePool里头要实现的接口：） <BR>闲话少说，来看代码~~ <BR>protected boolean minSizeInitialized = false; <BR><BR><BR>/** <BR>* Get an instance without identity. <BR>* Can be used by finders,create-methods, and activation <BR>* <BR>* @return Context /w instance <BR>* @exception RemoteException <BR>*/ <BR><BR>public EnterpriseContext get() { <BR>//pool里头有东东就拿出来 <BR>synchronized (pool) <BR>{ <BR>if (currentIndex &gt; -1) <BR>{ <BR>EnterpriseContext ctx = pool[currentIndex]; <BR>pool[currentIndex--] = null; <BR>return ctx; <BR>} <BR>} <BR><BR>//initialize a small fixed size of instance at startup. <BR>if (!minSizeInitialized) <BR>{ <BR>minSizeInitialized = true; <BR>synchronized (pool) <BR>{ <BR>for (int i = 0; i &lt; minSize; i++) <BR>{ <BR>pool[++currentIndex] = create(container.createBeanClassInstance()); <BR>} <BR>} <BR>} <BR><BR>// Pool is empty, create an instance <BR>return create(container.createBeanClassInstance()); <BR>} <BR><BR>/** <BR>* Return an instance after invocation. <BR>* <BR>* Called in 2 cases: <BR>* a) Done with finder method <BR>* b) Just removed <BR>* <BR>* @param ctx <BR>*/ <BR>public void free(EnterpriseContext ctx) { <BR>ctx.clear(); <BR>synchronized (pool) <BR>{ <BR>if (currentIndex + 1 &lt; maxSize) <BR>{ <BR>pool[++currentIndex] = ctx; <BR>} <BR>} <BR>} <BR>对于Entity...Pool,他overwrite了free <BR>/** <BR>* Return an instance to the free pool. Reset state <BR>* <BR>* 
<P>Called in 3 cases: <BR>* 
<UL><BR>* 
<LI>Done with finder method <BR>* 
<LI>Removed <BR>* 
<LI>Passivated <BR>* </LI></UL><BR>* <BR>* @param ctx <BR>*/ <BR>public void free(EnterpriseContext ctx) <BR>{ <BR>// If transaction still present don\\\\\\\\\\\\\\\'t do anything (let the instance be GC) <BR>if (ctx.getTransaction() != null) <BR>{ <BR>return ; <BR>} <BR>super.free(ctx); <BR>} <BR><BR>----------------------------------------------------- <BR>而Stateful....Pool的free方法overwrite了Abstract...Pool的， <BR>public synchronized void free(EnterpriseContext ctx) <BR>{ <BR>discard(ctx);//干脆就discard掉不用了~ <BR>} <BR><BR>剩下的下回再讲，先预告2个类体系： <BR>1.AbstractInstanceCache ,有EntityInstanceCache和StatefulSessionInstanceCache 子类。对于Entity,用它的 <BR>PrimaryKey作Cache的Key,对于Stateful,Jboss也会付给 <BR>每个instance一个唯一标定的值用来做CacheKey. <BR>Abstract...Cache与Abstract...Pool结合使用，得到好的Performance。 <BR>2.public abstract class Container, <BR>有EntityContainer,MessageDrivenContainer,Stateful/StatelessSessionContainer <BR>4个子类，用来提供对EJB instance的transaction/security/pool等服务。 <BR>//看看它的成员变量，就能猜个大概 <BR>/** This is the TransactionManager */ <BR>protected TransactionManager tm; <BR><BR>/** This is the SecurityManager */ <BR>protected AuthenticationManager sm; <BR><BR>/** This is the instancepool that is to be used */ <BR>protected InstancePool instancePool;</DIV>
<DIV>&nbsp;</DIV>
<DIV>开始讲Container,以前说过Container有4种子类，分别对应4种类型的EJB. <BR>一个Container是所有Container plugins(注1)和metadata(注2)的集散地，the container plugins可以从container拿到metadata和其他container plugins.EJB部署的时候会创建相应的Container.Container基本不做太多事，主要delegate给plugins作事情。 <BR>ok,让我们来看看Container的成员变量： <BR>/** <BR>* This is the new metadata. it includes information from both ejb-jar and <BR>* jboss.xml the metadata for the application can be accessed trough <BR>* metaData.getApplicationMetaData() <BR>*/ <BR>protected BeanMetaData metaData; <BR><BR>/** This is the EnterpriseBean class */ <BR>protected Class beanClass; <BR><BR>/** This is the Home interface class */ <BR>protected Class homeInterface; <BR><BR>/** This is the Remote interface class */ <BR>protected Class remoteInterface; <BR><BR>/** The local home interface class */ <BR>protected Class localHomeInterface; <BR><BR>/** The local inteface class */ <BR>protected Class localInterface; <BR><BR>/** This is the TransactionManager */ <BR>protected TransactionManager tm; <BR><BR>/** This is the SecurityManager */ <BR>protected AuthenticationManager sm; <BR><BR>/** This is the realm mapping */ <BR>protected RealmMapping rm; <BR><BR>/** This is the bean lock manager that is to be used */ <BR>protected BeanLockManager lockManager; <BR><BR>/** This is the application that this container is a part of */ <BR>protected EjbModule ejbModule; <BR>//ejbModule作为一个单元部署的Module,比如一个ejb-jar就是一个Module, <BR>/*这个 ejb-jar里头可能有多个entitybean,sessionbean,那么对于 每个entitybean,sessionbean <BR>都会有一个对应的container,而这些东东共享一个ejbModule.*/ <BR>/** <BR>* Returns a new instance of the bean class or a subclass of the bean class. <BR>* This factory style method is speciffically used by a container to supply <BR>* an implementation of the abstract accessors in EJB2.0, but could be <BR>* usefull in other situations. This method should ALWAYS be used instead <BR>* of getBeanClass().newInstance(); <BR>* <BR>* @return the new instance <BR>* <BR>* @see java.lang.Class#newInstance <BR>*/ <BR>public Object createBeanClassInstance() throws Exception { <BR>return getBeanClass().newInstance(); <BR>} <BR>public Class getBeanClass() <BR>{ <BR>return beanClass; <BR>} <BR><BR>注意EntityContainer overwrite了这个方法： <BR>/** <BR>* Returns a new instance of the bean class or a subclass of the bean class. <BR>* If this is 1.x cmp, simply return a new instance of the bean class. <BR>* If this is 2.x cmp, return a subclass that provides an implementation <BR>* of the abstract accessors. <BR>* <BR>* @see java.lang.Class#newInstance <BR>* <BR>* @return The new instance. <BR>*/ <BR>public Object createBeanClassInstance() throws Exception { <BR>return persistenceManager.createBeanClassInstance(); <BR>} <BR>其中 persistenceManager声明为： <BR>/** This is the persistence manager for this container */ <BR>protected EntityPersistenceManager persistenceManager; <BR>//persitenceManager和PersistenceStore我们将在第3部分讲解。 <BR>现在先给个大略印象： <BR>BMPPersistenceManager实现 <BR>public Object createBeanClassInstance() throws Exception { <BR>return con.getBeanClass().newInstance(); <BR>} <BR><BR>CMPPersistenceManager实现 <BR>EntityPersistenceStore store; <BR>public Object createBeanClassInstance() throws Exception <BR>{ <BR>return store.createBeanClassInstance(); <BR>} <BR>------------------------------------------------------------------- <BR>ok,接下来看看Container如何处理Client过来的Invocation。 <BR>一切精彩尽在下面这个函数 <BR>public Object invoke(Invocation mi)； <BR>//Invocation代表了Client端过来的调用 <BR>//Invocation里头有些成员变量，指明了要调用的Method, <BR>//args,Transaction信息，principle/credential等信息。 <BR><BR>/** Maps for MarshalledInvocation mapping */ <BR>protected Map marshalledInvocationMapping = new HashMap(); <BR><BR>public Object invoke(Invocation mi) <BR>throws Exception <BR>{ <BR>Thread currentThread = Thread.currentThread(); <BR>ClassLoader callerClassLoader = currentThread.getContextClassLoader(); <BR>//保存原来的classloader,在finally里恢复 <BR><BR>Method m = null; <BR>Object type = null; <BR><BR>try <BR>{ <BR>currentThread.setContextClassLoader(this.classLoader);(注3） <BR>// Check against home, remote, localHome, local, getHome, <BR>// getRemote, getLocalHome, getLocal <BR>type = mi.getType(); <BR><BR>if(type == InvocationType.REMOTE || <BR>type == InvocationType.LOCAL) <BR>{ <BR>if (mi instanceof MarshalledInvocation) <BR>{ <BR>((MarshalledInvocation) mi).setMethodMap( <BR>marshalledInvocationMapping); <BR>} <BR>return internalInvoke(mi); <BR>} <BR>else if(type == InvocationType.HOME || <BR>type == InvocationType.LOCALHOME) <BR>{ <BR>if (mi instanceof MarshalledInvocation) <BR>{ <BR>((MarshalledInvocation) mi).setMethodMap( <BR>marshalledInvocationMapping); <BR>return internalInvokeHome(mi); <BR>} <BR>else <BR>{ <BR>throw new MBeanException(new IllegalArgumentException( <BR>\\\\\\\"Unknown invocation type: \\\\\\\" + type)); <BR>} <BR>} <BR>finally <BR>{ <BR>currentThread.setContextClassLoader(callerClassLoader); <BR>} <BR>} <BR><BR>---------------------------------------------------------------- <BR>MarshalledInvocation是Invocation的字类，代表可以从 <BR>Client传到Server的Invocation <BR>public class Invocation... <BR><BR>public class MarshalledInvocation <BR>extends Invocation <BR>implements java.io.Externalizable <BR>而Invocation是在server端的调用链(Interceptor链，注4） <BR>间传递. <BR>------------------------------------------------------- <BR>ok,稍喘口气，接下来看看两个Internal的invoke, <BR>都是abstract,在字类实现 <BR>public abstract Object internalInvokeHome(Invocation mi) <BR>throws Exception; <BR>public abstract Object internalInvoke(Invocation mi) <BR>throws Exception; <BR>至于具体实现么，TO BE CONITUE拉:) <BR>---------------------------------------------------- <BR>注1：ContainerPlugin可以放在容器里头的东东。 <BR>接口为interface ContainerPlugin ： <BR>void setContainer(Container con); <BR>有InstancePool,InstanceCache,EJBProxyFactory/LocalProxyFactory, <BR>EntityPersistenceManager/EntityPersistenceStore等Plugin <BR>注2：metadata描述部署的信息，比如ejb-jar.xml，描述什么东东是 <BR>entitybean,什么东东是sessionbean,session的是BMP/CMP等等。 <BR>注3： Container的成员变量protected ClassLoader classLoader; <BR>用来load 这个Container里头的类和资源，之所以要专门设一个Container <BR>的classLoader是因为能使EJB re-deployable.(JBoss定期扫描deploy目录， <BR>如果ejb更改就进行redeploy,如果ejb删除就undeploy) <BR>注4： Jboss会建立一个Interceptor 链，Invocation经过链传递。 <BR>比如有EntityInterceptor，SecurityInterceptor，TransactionInterceptor， <BR>InvokerInterceptor，一个套一个，每个Interceptor对当前Invocation进行一些处理 <BR>，比如检查权限，事物等等，然后传给下一个Interceptor处理(这是Filter and Pipe模式了，也算是AOP拉~，别人说 <BR>JBoss 这个Interceptor实现属于AOP方言特别重：）。 <BR>所有的Interceptor都是现 <BR>Object invokeHome(Invocation mi) throws Exception; <BR>和Object invoke(Invocation mi) throws Exception; <BR>在自己invoke的最后， <BR>经过所有的Interceptor之后，调用下一个。 <BR>看看AbstractInterceptor的缺省实现： <BR>public abstract class AbstractInterceptor <BR>implements Interceptor <BR><BR>public Object invokeHome(final Invocation mi) throws Exception { <BR>//do sth. <BR>return getNext().invokeHome(mi); <BR>} <BR>public Object invoke(final Invocation mi) throws Exception { <BR>//do sth. <BR>return getNext().invoke(mi); <BR>} <BR>在经过重重Interceptor之后 <BR>最后到达EJB Instance 调用你要的那个方法。<BR></DIV>
<DIV>其实实现很简单： <BR>public Object internalInvokeHome(Invocation mi) throws Exception <BR>{ <BR>return getInterceptor().invokeHome(mi); <BR>} <BR><BR>public Object internalInvoke(Invocation mi) throws Exception <BR>{ <BR>// Invoke through interceptors <BR>return getInterceptor().invoke(mi); <BR>} <BR><BR>public Interceptor getInterceptor() <BR>{ <BR>return interceptor; <BR>} <BR>protected Interceptor interceptor; <BR>//这是Container建立的Interceptor链的头一个，从这里调起~ <BR>再来看看 void addInterceptor(Interceptor in);这个函数 <BR>在Interceptor链最后再挂一个Interceptor <BR>public void addInterceptor(Interceptor in) <BR>{ <BR>if (interceptor == null) <BR>{ <BR>interceptor = in; <BR>} <BR>else <BR>{ <BR>Interceptor current = interceptor; <BR>while (current.getNext() != null) <BR>{ <BR>current = current.getNext(); <BR>} <BR><BR>current.setNext(in); <BR>} <BR>} <BR>------------------------------------------------------------------------ <BR>附带再提一下pool和cache <BR>/** This is the instance cache for this container */ <BR>protected InstanceCache instanceCache; <BR><BR>/** This is the instancepool that is to be used */ <BR>protected InstancePool instancePool; <BR>/** This is the instancepool that is to be used */ <BR>protected InstancePool instancePool; <BR>------------------------------------------------------------- <BR>public void setInstanceCache(InstanceCache ic) <BR>{ <BR>if (ic == null) <BR>throw new IllegalArgumentException(\\\"Null cache\\\"); <BR><BR>this.instanceCache = ic; <BR>ic.setContainer(this); <BR>} <BR><BR>public InstanceCache getInstanceCache() <BR>{ <BR>return instanceCache; <BR>} <BR>public void setInstancePool(InstancePool ip) <BR>{ <BR>if (ip == null) <BR>throw new IllegalArgumentException(\\\"Null pool\\\"); <BR><BR>this.instancePool = ip; <BR>ip.setContainer(this); <BR>} <BR>----------------------------------------------------------------------- <BR>ok,现在让我们来看看Container都对EJB Instance暴露出来的 <BR>方法都作了些什么，还有如何调用EJB Instance的方法. <BR>这里有重要的2个Map <BR>/** <BR>* These are the mappings between the home interface methods and the <BR>* container methods. <BR>* 所有Home 方法映射都存这里 <BR>*/ <BR>protected Map homeMapping = new HashMap(); <BR><BR>/** <BR>* These are the mappings between the remote/local interface methods and the <BR>* bean methods. <BR>* 所有EJBObject方法映射都存这里 <BR>*/ <BR>protected Map beanMapping = new HashMap(); <BR>-------------------------------------------------------------------------- <BR>class ContainerInterceptor <BR>extends AbstractContainerInterceptor <BR>//AbstractContainerInterceptor基本上什么都不做，不用看 <BR>//ContainerInterceptor代表Container setup的Interceptor调用 <BR>//链的最后一个，到这里你就会看到他调用了你的EJB Instance的方法 <BR>{ <BR>public Object invokeHome(Invocation mi) throws Exception <BR>{ <BR>// Invoke and handle exceptions <BR>Method miMethod = mi.getMethod(); <BR>Method m = (Method) homeMapping.get(miMethod); <BR>if (m.getDeclaringClass().equals(EntityContainer.class)) <BR>{ <BR>try <BR>{ <BR>return m.invoke(EntityContainer.this, new Object[] { mi }); <BR>} <BR>catch (Exception e) <BR>{ <BR>rethrow(e); <BR>} <BR>} <BR>else // Home method <BR>{ <BR>try <BR>{ <BR>return m.invoke(((EnterpriseContext) mi.getEnterpriseContext()).getInstance(), mi.getArguments()); <BR>} <BR>catch (Exception e) <BR>{ <BR>rethrow(e); <BR>} <BR>} <BR><BR>// We will never get this far, but the compiler does not know that <BR>throw new org.jboss.util.UnreachableStatementException(); <BR>} <BR><BR>public Object invoke(Invocation mi) throws Exception <BR>{ <BR>// Get method <BR>Method miMethod = mi.getMethod(); <BR>Method m = (Method) beanMapping.get(miMethod); <BR>if( m == null ) <BR>{ <BR>String msg = \\\"Invalid invocation, check your deployment packaging\\\" <BR>+\\\", method=\\\"+miMethod; <BR>throw new EJBException(msg); <BR>} <BR><BR>// Select instance to invoke (container or bean) <BR>if (m.getDeclaringClass().equals(EntityContainer.class)) <BR>{ <BR>// Invoke and handle exceptions <BR>try <BR>{ <BR>return m.invoke(EntityContainer.this, new Object[]{ mi }); <BR>} <BR>catch (Exception e) <BR>{ <BR>rethrow(e); <BR>} <BR>} <BR>else <BR>{ <BR>// Invoke and handle exceptions <BR>try <BR>{ <BR>return m.invoke(((EnterpriseContext) mi.getEnterpriseContext()).getInstance(), mi.getArguments()); <BR>} <BR>catch (Exception e) <BR>{ <BR>rethrow(e); <BR>} <BR>} <BR><BR>// We will never get this far, but the compiler does not know that <BR>throw new org.jboss.util.UnreachableStatementException(); <BR>} <BR>} <BR><BR>//可以看到，两个Map作了个映射，Map的Key为 <BR>在你想要调用的EJB instance方法，value为实际 <BR>实现的方法，可能就是EJB instance本身实现的方法， <BR>或者是容器帮忙实现的(比如CMP 中abstract get/set方法， <BR>只能容器帮忙实现). <BR>从这里也可以看出来，JBoss主要是保存了方法映射来处理 <BR>EJBObject/EJBLocalObject 的调用请求, <BR>而其他一些J2EE AS是通过动态生成EJBObject/EJBLocalObject <BR>和你的EJB Instance的字类来实现的（而JBoss就算在CMP2.0 <BR>里动态生成了一个东东，那也不是EJB Instance的子类)。</DIV>
<DIV>&nbsp;</DIV>
<DIV>ok,基本弄明白了Container的原理之后，我们来看看 <BR>到底Container的一些初始化操作 <BR>Container算是一项服务， <BR>JBoss在deploy/undeploy/redeploy时会调用 <BR>与Service相关的几个函数： <BR>protected void createService() throws Exception {} <BR>protected void startService() throws Exception {} <BR>protected void stopService() throws Exception {} <BR>protected void destroyService() throws Exception {} <BR>让我们从EntityContainer看起： <BR><BR>protected void createService() throws Exception <BR>{ <BR>// Associate thread with classloader <BR>ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); <BR>Thread.currentThread().setContextClassLoader(getClassLoader()); <BR><BR>try <BR>{ <BR>// Acquire classes from CL <BR>//从metadata拿到Home/Remote的Class <BR>if (metaData.getHome() != null) <BR>homeInterface = classLoader.loadClass(metaData.getHome()); <BR>if (metaData.getRemote() != null) <BR>remoteInterface = classLoader.loadClass(metaData.getRemote()); <BR><BR>// Call default init <BR>// 调用Container里头的CreateService,我们回头再看 <BR>super.createService(); <BR><BR>//建立刚才所说的两个Method映射Map <BR>setupBeanMapping(); <BR>setupHomeMapping(); <BR><BR>// Map the interfaces to Long <BR>setupMarshalledInvocationMapping(); <BR><BR>// Initialize pool <BR>instancePool.create(); <BR>// Try to register the instance pool as an MBean <BR>try <BR>{ <BR>ObjectName containerName = super.getJmxName(); <BR>Hashtable props = containerName.getKeyPropertyList(); <BR>props.put(\\\"plugin\\\", \\\"pool\\\"); <BR>ObjectName poolName = new ObjectName(containerName.getDomain(), props); <BR>server.registerMBean(instancePool, poolName); <BR>} <BR>catch(Throwable t) <BR>{ <BR>log.debug(\\\"Failed to register cache as mbean\\\", t); <BR>} <BR><BR>// Init instance cache <BR>instanceCache.create(); <BR>// Try to register the instance cache as an MBean <BR>try <BR>{ <BR>ObjectName containerName = super.getJmxName(); <BR>Hashtable props = containerName.getKeyPropertyList(); <BR>props.put(\\\"plugin\\\", \\\"cache\\\"); <BR>ObjectName cacheName = new ObjectName(containerName.getDomain(), props); <BR>server.registerMBean(instanceCache, cacheName); <BR>} <BR>catch(Throwable t) <BR>{ <BR>log.debug(\\\"Failed to register cache as mbean\\\", t); <BR>} <BR><BR><BR>for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); ) <BR>{ <BR>String invokerBinding = (String)it.next(); <BR>EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding); <BR>ci.create(); <BR>} <BR><BR><BR><BR>// Init persistence <BR>persistenceManager.create(); <BR><BR>// Initialize the interceptor by calling the chain <BR>Interceptor in = interceptor; <BR>while (in != null) <BR>{ <BR>in.setContainer(this); <BR>in.create(); <BR>in = in.getNext(); <BR>} <BR>readOnly = ((EntityMetaData)metaData).isReadOnly(); <BR>} <BR>finally <BR>{ <BR>// Reset classloader <BR>Thread.currentThread().setContextClassLoader(oldCl); <BR>} <BR>} <BR><BR><BR><BR>protected void startService() throws Exception <BR>{ <BR>// Associate thread with classloader <BR>ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); <BR>Thread.currentThread().setContextClassLoader(getClassLoader()); <BR><BR>try <BR>{ <BR>// Call default start <BR>super.startService(); <BR><BR>// Start container invokers <BR>for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); ) <BR>{ <BR>String invokerBinding = (String)it.next(); <BR>EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding); <BR>ci.start(); <BR>} <BR><BR>// Start instance cache <BR>instanceCache.start(); <BR><BR>// Start persistence <BR>persistenceManager.start(); <BR><BR>// Start the instance pool <BR>instancePool.start(); <BR><BR>// Start all interceptors in the chain <BR>Interceptor in = interceptor; <BR>while (in != null) <BR>{ <BR>in.start(); <BR>in = in.getNext(); <BR>} <BR>} <BR>finally <BR>{ <BR>// Reset classloader <BR>Thread.currentThread().setContextClassLoader(oldCl); <BR>} <BR>} <BR><BR>protected void stopService() throws Exception <BR>{ <BR>// Associate thread with classloader <BR>ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); <BR>Thread.currentThread().setContextClassLoader(getClassLoader()); <BR><BR>try <BR>{ <BR>//Stop items in reverse order from start <BR>//This assures that CachedConnectionInterceptor will get removed <BR>//from in between this and the pm before the pm is stopped. <BR>// Stop all interceptors in the chain <BR>Interceptor in = interceptor; <BR>while (in != null) <BR>{ <BR>in.stop(); <BR>in = in.getNext(); <BR>} <BR><BR>// Stop the instance pool <BR>instancePool.stop(); <BR><BR><BR>// Stop persistence <BR>persistenceManager.stop(); <BR><BR>// Stop instance cache <BR>instanceCache.stop(); <BR><BR>// Stop container invoker <BR>for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); ) <BR>{ <BR>String invokerBinding = (String)it.next(); <BR>EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding); <BR>ci.stop(); <BR>} <BR><BR>// Call default stop <BR>super.stopService(); <BR>} <BR>finally <BR>{ <BR>// Reset classloader <BR>Thread.currentThread().setContextClassLoader(oldCl); <BR>} <BR>} <BR><BR>protected void destroyService() throws Exception <BR>{ <BR>// Associate thread with classloader <BR>ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); <BR>Thread.currentThread().setContextClassLoader(getClassLoader()); <BR><BR>try <BR>{ <BR>// Destroy container invoker <BR>for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); ) <BR>{ <BR>String invokerBinding = (String)it.next(); <BR>EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding); <BR>ci.destroy(); <BR>} <BR><BR>// Destroy instance cache <BR>instanceCache.destroy(); <BR>instanceCache.setContainer(null); <BR>try <BR>{ <BR>ObjectName containerName = super.getJmxName(); <BR>Hashtable props = containerName.getKeyPropertyList(); <BR>props.put(\\\"plugin\\\", \\\"cache\\\"); <BR>ObjectName cacheName = new ObjectName(containerName.getDomain(), props); <BR>server.unregisterMBean(cacheName); <BR>} <BR>catch(Throwable ignore) <BR>{ <BR>} <BR><BR>// Destroy persistence <BR>persistenceManager.destroy(); <BR>persistenceManager.setContainer(null); <BR><BR>// Destroy the pool <BR>instancePool.destroy(); <BR>instancePool.setContainer(null); <BR>try <BR>{ <BR>ObjectName containerName = super.getJmxName(); <BR>Hashtable props = containerName.getKeyPropertyList(); <BR>props.put(\\\"plugin\\\", \\\"pool\\\"); <BR>ObjectName poolName = new ObjectName(containerName.getDomain(), props); <BR>server.unregisterMBean(poolName); <BR>} <BR>catch(Throwable ignore) <BR>{ <BR>} <BR><BR>// Destroy all the interceptors in the chain <BR>Interceptor in = interceptor; <BR>while (in != null) <BR>{ <BR>in.destroy(); <BR>in.setContainer(null); <BR>in = in.getNext(); <BR>} <BR><BR>MarshalledInvocation.removeHashes(homeInterface); <BR>MarshalledInvocation.removeHashes(remoteInterface); <BR><BR>// Call default destroy <BR>super.destroyService(); <BR>} <BR>finally <BR>{ <BR>// Reset classloader <BR>Thread.currentThread().setContextClassLoader(oldCl); <BR>} <BR>} <BR>------------------------------------------------------------ <BR>protected void setupBeanMapping() throws Exception <BR>{ <BR>try { <BR>if (remoteInterface != null) <BR>{ <BR>Method[] m = remoteInterface.getMethods(); <BR>setupBeanMappingImpl( m, \\\"javax.ejb.EJBObject\\\" ); <BR>} <BR>if (localInterface != null) <BR>{ <BR>Method[] m = localInterface.getMethods(); <BR>setupBeanMappingImpl( m, \\\"javax.ejb.EJBLocalObject\\\" ); <BR>} <BR>} <BR>catch (Exception e) <BR>{ <BR>// ditch the half built mappings <BR>homeMapping.clear(); <BR>beanMapping.clear(); <BR><BR>throw e; <BR>} <BR>} <BR><BR>private void setupBeanMappingImpl( Method[] m, String intfName ) <BR>throws Exception <BR>{ <BR>for (int i = 0; i &lt; m.length; i++) <BR>{ <BR>if (!m<I>.getDeclaringClass().getName().equals(intfName)) <BR>{ <BR>// Implemented by bean <BR>beanMapping.put(m<I>, beanClass.getMethod(m<I>.getName(), m<I>.getParameterTypes())); <BR>} <BR>else <BR>{ <BR>// Implemented by container <BR>beanMapping.put(m<I>, getClass().getMethod(m<I>.getName(), <BR>new Class[] { Invocation.class })); <BR>} <BR>} <BR>} <BR>private void setupHomeMappingImpl(Method[] m, <BR>String finderName, <BR>String append) <BR>throws Exception <BR>{ <BR>// Adrian Brock: This should go away when we don\\\'t support EJB1x <BR>boolean isEJB1x = metaData.getApplicationMetaData().isEJB1x(); <BR><BR>for (int i = 0; i &lt; m.length; i++) <BR>{ <BR>String methodName = m<I>.getName(); <BR>try <BR>{ <BR>try // Try home method <BR>{ <BR>String ejbHomeMethodName = \\\"ejbHome\\\" + methodName.substring(0,1).toUpperCase() + methodName.substring(1); <BR>homeMapping.put(m<I>, beanClass.getMethod(ejbHomeMethodName, m<I>.getParameterTypes())); <BR><BR>continue; <BR>} <BR>catch (NoSuchMethodException ignore) {} // just go on with other types of methods <BR><BR><BR>// Implemented by container (in both cases) <BR>if (methodName.startsWith(\\\"find\\\")) <BR>{ <BR>homeMapping.put(m<I>, this.getClass().getMethod(finderName, new Class[] { Invocation.class })); <BR>} <BR>else if (methodName.equals(\\\"create\\\") || <BR>(isEJB1x == false &amp;&amp; methodName.startsWith(\\\"create\\\"))) <BR>{ <BR>homeMapping.put(m<I>, this.getClass().getMethod(\\\"create\\\"+append, new Class[] { Invocation.class })); <BR>beanMapping.put(m<I>, this.getClass().getMethod(\\\"postCreate\\\"+append, new Class[] { Invocation.class })); <BR>} <BR>else <BR>{ <BR>homeMapping.put(m<I>, this.getClass().getMethod(methodName+append, new Class[] { Invocation.class })); <BR>} <BR>} <BR>catch (NoSuchMethodException e) <BR>{ <BR>throw new NoSuchMethodException(\\\"Could not find matching method for \\\"+m<I>); <BR>} <BR>} <BR>} <BR><BR>protected void setupHomeMapping() throws Exception <BR>{ <BR>try { <BR>if (homeInterface != null) <BR>{ <BR>Method[] m = homeInterface.getMethods(); <BR>setupHomeMappingImpl( m, \\\"find\\\", \\\"Home\\\" ); <BR>} <BR>if (localHomeInterface != null) <BR>{ <BR>Method[] m = localHomeInterface.getMethods(); <BR>setupHomeMappingImpl( m, \\\"findLocal\\\", \\\"LocalHome\\\" ); <BR>} <BR><BR>// Special methods <BR><BR>// Get the One on Handle (getEJBObject), get the class <BR>Class handleClass = Class.forName(\\\"javax.ejb.Handle\\\"); <BR><BR>// Get the methods (there is only one) <BR>Method[] handleMethods = handleClass.getMethods(); <BR><BR>//Just to make sure let\\\'s iterate <BR>for (int j=0; j<HANDLEMETHODS.LENGTH <br ;j++)> { <BR>//Get only the one called handle.getEJBObject <BR>if (handleMethods[j].getName().equals(\\\"getEJBObject\\\")) <BR>{ <BR>//Map it in the home stuff <BR>homeMapping.put(handleMethods[j], <BR>this.getClass().getMethod(\\\"getEJBObject\\\", <BR>new Class[] {Invocation.class})); <BR>} <BR>} <BR>} <BR>catch (Exception e) <BR>{ <BR>// ditch the half built mappings <BR>homeMapping.clear(); <BR>beanMapping.clear(); <BR><BR>throw e; <BR>} <BR>}</I></I></I></I></I></I></I></I></I></I></I></I></I></I></DIV><B><BR></B><img src ="http://www.blogjava.net/kapok/aggbug/2569.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-29 09:46 <a href="http://www.blogjava.net/kapok/archive/2005/03/29/2569.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>