门户系统Portlet开发技术解决方案


门户系统 Portlet 开发技术解决方案 Portlet 开发技术解决方案 编 写: 付斌 QQ:325541232 2008 年 11 月 20 日 门户系统 Portlet 开发技术解决方案 目 录 1. 方案说明 ........................................................................................................................... 4 2. 理论概述 ........................................................................................................................... 4 2.1. Java Portlet 规范(JSR-168) ............................................................................... 4 2.2. Portal概述 ............................................................................................................... 5 2.2.1. Portal Server .......................................................................................................... 5 2.2.2. Portlet Container .................................................................................................... 7 2.2.3. Portlet ..................................................................................................................... 7 2.3. 应用名词 ................................................................................................................. 26 2.3.1. URLs编码 ......................................................................................................... 26 2.3.2. 呈现参数(Render Parameters) ............................................................... 27 2.3.3. 重定向 ............................................................................................................. 27 2.3.4. 会话(Sessions) ......................................................................................... 28 2.3.5. 请求(Request) ................................................................................................ 33 2.3.6. 响应(Response) ......................................................................................... 37 2.3.7. 上下文(Context) ....................................................................................... 39 2.3.8. 缓存(Caching) ............................................................................................... 42 2.3.9. 模式(Model) ............................................................................................... 44 2.3.10. 应用程序(Application) ......................................................................... 45 2.3.11. 内容类型(Content Types) ..................................................................... 49 2.3.12. 安全(Security) ....................................................................................... 68 2.3.13. 接口(Interface) ..................................................................................... 77 3. 通用开发方案 ................................................................................................................. 79 3.1. 运行环境 ................................................................................................................. 79 3.1.1. 硬件环境 ......................................................................................................... 79 3.1.2. 软件环境 ......................................................................................................... 79 3.2. 配置步骤 ................................................................................................................. 80 3.2.1. 配置测试环境 ................................................................................................. 80 门户系统 Portlet 开发技术解决方案 3.2.2. 安装配置开发环境 ......................................................................................... 80 3.3. 开发步骤 ................................................................................................................. 89 3.3.1. 在Eclipse3.3(lomboz)中开发 ................................................................. 89 3.3.2. 在my-eclipse6.6 中开发 ............................................................................ 106 4. LiferayPortlet开发方案 ........................................................................................... 119 4.1. 方案说明 ............................................................................................................... 119 4.2. 理论概述 ............................................................................................................... 120 4.2.1. Liferay Portal ..................................................................................................... 120 4.2.2. Liferay 描述 ............................................................................................... 120 4.3. 安装配置步骤 ....................................................................................................... 165 4.3.1. 开发SDK安装 ................................................................................................. 165 4.3.2. 环境变量配置 ............................................................................................... 166 4.3.3. 配置文件修改 ............................................................................................... 166 4.4. 开发步骤 ............................................................................................................... 167 4.4.1. plugins开发方式 ......................................................................................... 167 4.5. 开发进阶 ............................................................................................................... 175 4.5.1. 创建Struts Portlet ................................................................................... 175 4.5.2. 部署Portlet ................................................................................................. 199 门户系统 Portlet 开发技术解决方案 1. 方案说明 此解决方案专门为从事开发符合 JSR168 规范的 Portlet 的项目组人员提供 技术资料支持,主要的开发详细说明全部在附录中其内容可行性经过实际测试, 如出现问题请以 JSR-168API 为最终解释文件。 汉化资源搜集整理于网上及参照 JSR168API 并由本人作了些修改和添加,主 要为 Portlet 开发提供真实依据,由于个人水平有限,如有错误,请各位高手指 正。 2. 理论概述 2.1. Java Portlet 规范(JSR-168) Java 平台提供了一种基于JSR-168的标准,标准化portlets如何与其他 portlet 容器集成并在不同的portal 产品之间来保证兼容性. LiferayPortal 提供了一种100%便利的portlet 容器来保证任何基于标准规范的portlet将会在 Liferay中运行. 在JSR-168规范中,一个portlet应用可以聚集其他的不同的portlets并且打包成 一个WAR文件,就像一个标准的Java web应用一样.在WAR文件中,以portlet.xml 来定义portlets 的应用, portlet.xml 文件放在WAR 文件中的WEB-INF目录中. Java Portlet 规范包括portlet 容器.更多关于JSR-168的资料推荐阅读规范本 身. 并且在网上也有一些介绍JSR-168 portlets的开发文章和解释如何应用不 同的porltet框架的文章. 门户系统 Portlet 开发技术解决方案 2.2. Portal概述 Portal 的主要组成可以分为三部份: 2.2.1. Portal Server 2.2.1.1. Portal Server 的定义 一个 Portal(门户网站)就是指一个 Web-based 的系统,通常都会提供个 人化设置、单一登陆、以及由各种不同来源或不同网站取得各式各样的信息,并 且将这些信息放在网页之中组合而成的呈现平台,门户网站会有精巧的个人化设 置去提供定制的网页,当不同等级的使用者来浏览该页面将获得不同的信息内 容。 2.2.1.2. Portal页面的结构 一个portlet能产生标记片段。portal通常在portlet产生的标记片段中加上 标题,控制按钮及其它修饰。这个新的标记片段称为portlet窗口。然后portal 合并这些portlet窗口为一个完整的文档,即portal页面 标题 修饰和控件 portlet 片段 portlet窗口 portal页面 门户系统 Portlet 开发技术解决方案 2.2.1.2.1. 门户页创建 portlets 在 portlet container 内执行,portlet container 接收 portlets产生的内容。通常 portlet container 将这些内容提交给 portal server,portal server 从这些内容建立 portal page 然后将它传给客户端。 2.2.1.2.2. 门户页请求序列 使用者经由客户端设备(例如浏览器)存取 portal,portal 根据接收到的 request 决定哪些 portlets 需要被执行以满足需求。portal 通过 portlet container 呼叫 portlets,然后由 portlets 产生的片段建立 portal page, 再传回客户端呈现给使用者。 门户系统 Portlet 开发技术解决方案 2.2.2. Portlet Container 2.2.2.1. Portlet Container 的定义 portlet container 是提供 portlets 执行的环境,包含了许多 portlets 并且管理他们的生命周期,他也会永远保存着 portlets 的喜好设置,一个 portlet container 接收到来自 portal 的请求后,接着将这个请求传递给存在 container 的 portlet 执行。portlet container 没有义务去组合 portlets 产生的信息內容,这个工作必须由 portal 来处理。portal 和 portlet container 可以放在一起视为同一个系统的组件,或者分开成为两个独立的组 件。 2.2.2.1.1. Servlet 容器和 Portlet 容器的关系 portlet容器是servlet容器的扩展,所以一个portlet容器可以构建于一个 已存在的servlet容器之上或者可能实现servlet容器的全部功能。无论portlet 容器如何实现,它的运行环境总是假定它支持Servlet 2.3规范。 2.2.3. Portlet 2.2.3.1. Portlet定义 一个 Portlet 是以 Java 技术为技术的 Web 组件,由 Portlet Container 所管理,专门处理客户的 request 以及产生各种动态的信息内容。Portlets 为 可插式 ( pluggable ) 的客户界面组件,提供呈现层成为一个信息系统。 这些由 portlet 产生的内容也被称为片段 (fragment),而片段是具有一些 规则的Markup( HTML、XHTML、WML ),而且可以和其他的片段组合而成一个复杂 的文件。而 Portlet 中的内容正常来说是与其他 Portlet 的内容聚合而成为一 个 Portal 网页。而 Portlet 的生命周期是被 Portlet Container 所管理控制 的。 客户端和 portlets 的互动是由 portal 通过典型的 request/response 门户系统 Portlet 开发技术解决方案 方式实现,正常来说,客户会和 portlets 所产生的内容互动,举例来说,根据 下一步的连接或者是确认送出的表单,结果 portal 将会接收到 portlet 的动 作,将这个处理状况转向到目标 portlet。这些 portlet 内容的产生可能会因 为不同的使用者而有不同的变化,完全是根据客户对于这个 portlet 的设置。 2.2.3.2. Portlet 与Servlet 的区别 开发一个Java Portlet 类似于开发一个基于web应用的Servlet开发,主要的区 别在于:  Portlet只能生成标记段,不是整个文档, Portal将会连接一些portlets的标 记段并且创建一个整个页面返回给用户.  接受请求的类必须继承javax.portlet.Portlet 而不是 javax.servlet.http.HttpServlet.  一个portlet请求可能涉及两个状态进程: 1. 对于大多数的请求来说,一个portlet将会执行一个状态转换操作,这与 action 状态有关系,并且通过一个方法processAction来实现.并不是需要每一 个Action 状态.包含一个action 状态的请求被注入到ActionURL,与此同时那些 只含有render phase 的请求被注入到RenderURL. 2. 接下来所有与页面有关联的,将要被返回的其他的portlets的render状态可 能会在任何将HTML标记段来描述他们自身的请求中来被调用.Portlet通过一个 render方法实现这种功能. 现有的web应用开发例如Struts,JSF,Webworks, etc可以通过桥接的使用来实现, 其他的框架可以直接使用无需通过桥接, Spring Portlet MVC 框架就是一个例 子. 1. 一个portlet容器是一个类似服务器的软件组件,这个组件可以运行 portlets. 2. 一个portal是一个web 应用,包括portlet容器并且给用户提供额外的服 务. 门户系统 Portlet 开发技术解决方案 2.2.3.3. portlet 与 servlet 的关系 Portlet 和 Servlet 算是兄弟有那么一点点相似却又有那么一点点不同, 因为 Servlet 和 Portlet 不尽然相同,所以研究小組決定将 portlets 定义成 为一个新的组件,因此定义了 portlets 一个新的并且明确的界面与行为。为了 尽可能与现有的 servlet 结合达到重复使用的目的,portlet 的规范利用了 servlet 的规范,许多观念都很相似的,结合 portlets、servlets 及 jsp 在 同一个网站系统中,我们称为 portlet application 。在同一个 portlet application 中,他们将分享同一个 classloader,context 及 session。 2.2.3.3.1. 相似之处 @ portlets 也是 Java 技术的 web 组件 @ portlets 也是有特定的 container 在管理 @ portlets 可以动态产生各种内容 @ portlets 的生命周期由 container 所管理 @ portlets 和客户端的互动是通过 request/response 的机制 2.2.3.3.2. 不同之处 @ portlets 只产生 markup 信息片段,不是完整的网页文件。而 Portal 会 将所有的 Portlet markup 信息片段放到一个完整的 Portal 网页。 @ portlets 不会和 URL 有直接的关系 @ 客户端必须通过 portal 系统才能和 portlets 互动 @ portlets 有一些定义好的 request 处理,action request 以及 render request。 @ portlets 默认定义 portlet modes 及窗口状态可以指出在网页中该 portlet 的哪个功能正在执行及现在的 状态。 @ portlets 可以在同一个 portal 网页之中存在多个。 2.2.3.3.3. 附加功能 门户系统 Portlet 开发技术解决方案 @ Portlets 能够存取及储存永久配置文件及定制资料。 @ portlets 可以存取使用者数据 @ portlets 具有 URL 的重写功能在文件中去动态建立连结,允许 portal server 不用去知道如何在网页的片 段之中建立连结及动作。 @ portlets 可以储存临时性的数据在 portlet session 之中,拥有两个不同 的范围 : application-wide scope 及 portlet private scope 。 2.2.3.3.4. 不足之处 @ servlet 具有设置输出的文字编码( character set encoding)方式 @ servlet 可以设置 HTTP 输出的 header @ servlet 才能够接收客户对于 portal 发出的 URL 请求 2.2.3.3.5. Portlet 实例数 Portlet Container 如何产生 portlet instances 是被布署描述 (deployment descriptor)中 portlet 的定义所控制的。在没有分布式的环境中(默 认情况),portlet container 必须实例化一个并且只能使用一个 portlet 物件来对应一 个 portlet 定义。 另一方面,在某种情况下,portlet在web.xml部署描述文件中被部署为分布 式环境下的portlet应用程序的一部份。那么,在同一虚拟机(VM)中,同一部 署描述文件内,同一portlet定义下,一个portlet容器只能实例该portlet一次。 2.2.3.3.6. Portlet 生命周期 一个portlet有着良好的生命周期管理,定义了怎样装载,实例化和初始化, 怎样握持来自客户端的请求及怎样送出服务。这个portlet生命周期由portlet 接口的init,processAction,render和destroy方法来表达。 门户系统 Portlet 开发技术解决方案 2.2.3.4. Portlets 与 Servlets/ JSPs 的关联 2.2.3.4.1. 调用 servlets JSPs 和 JSPs 标签库 一个 portlet 可以使用请求发送者来调用 servlets 和 JSPs ,就像 servlet 使用调用其它 servlets 和 JSPs 一样。为了使portlets和servlets 之间整合得天衣无缝,Portlet规范允许调用更多的servlet对象。 当servlet或JSP在portlet中被调用时,传给servlet或JSP的request是以 portlet request为基础的。同样,传给servlet或JSP的response是以portlet response为基础的。 被包括的servlet request可使用portlet request的Attributes设置。 portlet和被包括的servle或JSP分享同一个的输出流。 portlet 会话中的Attributes设置可以来自于servlet会话,反之亦然。 2.2.3.5. Portlet 接口 Portlet interface 是 Portlet API 中主要的抽象接口,所有的 portlet 不是直接操作这个接口,就是继承了这个接口类。 Portlet API 包含 GenericPortlet 类,此类提供了一些预设的功能;开发 人员应该继承、直接或间接地拓展 GenericPortlet 类,以写出自己的 portlet。 2.2.3.6. 加载和实例化 portlet container 负责载入和实例化 portlet。当 portlet container 运 行 portlet application 或者延迟至 portlet 需要服务使用者请求时, portlet 就会被载入和实例化。 在 Web application 下的 portlet application 中,portlet container 用 来载入 portlet 的 ClassLoader 和 Servlet container 的 ClassLoder 是相 同的。载入 portlet class 后, portlet class 就被实例化。 门户系统 Portlet 开发技术解决方案 2.2.3.7. 初始化 portlet 物件实例化后,portlet container 还必须初始化 portlet,以调 用 portlet 去处理客户端要求。 Protlet 经由初始化完成初始高代价的资源(例如在背景扫行的连结),和 执 行其他只要执行一次的初始工作。 portlet container 呼叫 Portlet 接口中 init 方法来初始化 portlet, init 方法中 portlet 的定义由拓展 PortletConfig 接口的物件来提供。此 configuration 物件可取出定义在部署描述的初始化参数(initialization parameter)及 Resource Bundle 。此 configuration 物件亦提供存取描述 portlet runtime 环境的上下文(context)物件。 要了解 PortletConfig 接口的详细信息,请参考 Portlet Config 章节。 要了解 PortletContex 接口的详细信息,请参考 Portlet Context 章节。 2.2.3.8. 初始化参数 通过 PortletConfig 这个 interface 所提供的 getInitParameterNames 和 getInitParameter 这两个方法, 可以得知在 portlets 部署描述文件中所 定义的 portlet 初始参数及初始值。 dummyName dummyValue 门户系统 Portlet 开发技术解决方案 2.2.3.9. 错误条件的初始化 在 portlet 初始化期间,portlet 物件可能会丟出 UnavailableException 或 PortletException 异常。此时,portlet container 不能把 portlet 物件 置入已启动的服务,并且 portlet container 必需释放这个 portlet 物件。 destory 方法不能被呼叫,因为初始化被认为执行失败。发生 failure 后, portlet container 会尝试着重新实例化及初始化 portlet。这个异常处理的规 则是:由一个 UnavailableException 指定一个不能执行的最小时间,当此异常 发生时,portlet container 必需等到指定时间过去后才产生并且初始化一个新 的 portlet 物件。 在初始化过程中所丟出的 Runtime Exception 异常,必需当作 PortletException 来处理。 2.2.3.10. Portlet 配置 PortletConfig 物件,提供了在初始化一个 portlet 时所需要的信息。同 时可以通过此物件取得 portlet context 和 提供 portlet title-bar 信息的 resource bundle。 译者注:这里提到的“信息”指的是 title, short-title, keyword。 2.2.3.11. Portlet 资源捆绑 在 Portlets 的部署描述文件中, 可以设定 portlet 的基本信息如 portlet title-bar 的名称及 portlet 在 portal 中的分类名称。为了显示这 些信息, Portlet Spec. 定义了一些 resource elements 让 title , short-title, keyword 可以根据 Locale 来显示。 (see the PLT.21.10 Resource Bundles Section) 在 portlets 部署描述文件中,这些 resource elements 可以直接放在 门户系统 Portlet 开发技术解决方案 portlet 的定义中, 亦或是写在 resource bundle(*.properties) 里。 以下是一个定义 portlet 信息的例子: Stock Quote Portlet Stock finance, stock market 假如这些信息的定义是写在 resource bundle 中的话, 此时这个 portlet 必须提供一个 resource bundle 的名字。 示例如下: com.foo.myApp.QuotePortlet … 假如 portlet 定义提供了一个 resource bundle 的话, portlet-container 则必须通过 ResourceBundle 来 look up 这些设定值。 门户系统 Portlet 开发技术解决方案 假如 root resource bundle 没有设定这些信息, 且同时这些信息是写 在的话, portlet container 必须把他们都设到 root resource bundle 里。TCK PORTLET:SPEC:24 提到:假如使用 ResourceBundle 资源文件 来定义,则 Portlet container 必须先 look up 在 ResourceBundle 里定义的 信息。若是 ResourceBundle 没有这些信息,或是没有使用 ResourceBundle 来 定, 则 portlet container 必须 look up 写在定义文件里的信息。 假如 ResourceBundle 和定义文件里都没有定义这些信息, 则 portlet container 必须以空字符串来回传。 假如在定义 portlet 时,没有定义 resource bundle,而是把信息定义在部 署定义文件中,此时 portlet container 则必须产生一个 ResourceBundle ,并且把这些信息放进来。 而使用的 key 值必须遵照 PLT.21.10 Resource Bundles Section 所定义的。 GenericPortlet 的 render method 在取得 portlet 的 title 名称时,就 是使用 PortletConfig 的 ResourceBundle 物件,通过相关的 ResourceBundle 设定或是直接写在 portlet 部署描述文件中的资料而来。 2.2.3.12. Portlet URLs Portlet 也许会在自己的内容里出现 URL link, 同时这个 URL link 是参 考到自己本身。比如说, 当 User 在操作一个 portlet 里面的 URL 时(也就是 说点选一个链接或是 submit 一张表单)。 对 portal 而言, 此时是一个新的 请求, 而这个请求的目标就是那一个 portlet。 那些 URLs 就称作为 portlet URLs。 2.2.3.13. PortletURL Portlet API 定义了一个 PortletURL 的接口。Portlets 必须用这个 PortletURL 物件来产生 portlet URLs 。 在产生 PortletURL 的同时, PortletURL 也会呼叫 RenderResponse 接口定义的两个方法: createActionURL 和 createRenderURL。 createActionURL 用来生成 action URLs。 门户系统 Portlet 开发技术解决方案 createRenderURL 用来生成 render URLs。 由于一些 portal/portlet-containers 在实际操作时可能会附加一些 query string 在 url link 上, 以期提供这些 container 所需要的一些内部 状态的参数等等, 所以 portlet 开发者不应指定表单的传送方法为 HTTP GET。 ex: WebLogic 会对 url link 加上一些参数的例子。 http://localhost:7001/my.portal?_nfpb=true&_pageLabel=book_33 一个 render URL 是对某些特殊类型 action URLS 的一种优化。 在 render portlet URL 的过程中 Portal/portlet-container 不允许调用目标 portlet 的 processAction 方法。 Portal/portlet-container 必须确保当在建构 render URL 时所设定的参数, 也能变成是在请求这个 portlet 时的参数。 Render URLs should not be used for tasks that are not idempotent from the portlet perspective。 点击 render 过的 URL 发出的请求, portlet 所产生的结果有可能会因为 错误状况, 缓存失效, 外部资料的改变等等, 而有所影响。 Render URLs 不 应放在 form 表单里, 因为 portal/portlet-container 有可能因此而忽略 form 表单的参数。 Portlets 可以通过 PortletURL 所提供的 setParameter 和 setParameters 两个方法, 对 PortletURL 物件设定一些应用程序的特殊参数。 setParameter 这个方法必须把以前所设定的同名参数给取代掉。所有 portlet 所加到 PortletURL 物件上的参数都必须为该 portlet 的可用请求参数。 Portlet 开发者必须注意到 render request 的请求参数并非在产生 PortletURL 时就被使用到。 portlet-container 必须把加到 portletURL 物件上的参数名和值以 “x-www-form-urlencoded“方式编码。 在这些参数名和值被添加到 portletUR 物件上之前,portlet 开发人员不可以编 门户系统 Portlet 开发技术解决方案 码它们。 如果 protal/portlet-container 输入一些额外的信息来当作参数, 它必 须适当地输入这些参数, 以避免这些由 portlet 所设定和使用的参数产生碰撞 或冲突。 运用 toString 这个方法, portlet 可以获得 PortletURL 在该 portlet 里所表示的字符串。 以下是个产生 portlet URI 的例子: ... PortletURL url = response.createRenderURL(); url.setParameter(“customer”,”foo.com”); url.setParameter(“show”,”summary”); writer.print(“Summary”); ... Portlet 开发者应该知道 PortletURL 所表现的字符串并不是个很好看的 表单 URLS, 但却是一个 portlet 在此时生成内容的的特別象征。 Portlet servers 通常用一种 URL 重写的技术来对真正的 URL 进行处理加工。 门户系统 Portlet 开发技术解决方案 2.2.3.13.1. 包括一个 Portlet 模式或一个窗口状态 Portlet URL 可以包含一个特定的 portlet mode (参考 PLT.8 Portlet Modes Chapter) 或 window state (参考 PLT.9 Window States Chapter). 这 个 PortletURL 接口提供 setWindowState 和 setPortletMode 方法,在 URL 里加入一些参数来设定 portlet mode 和 window state。 例如: ... PortletURL url = response.createActionURL(); 25 url.setParameter(“paymentMethod”,”creditCardInProfile”); url.setWindowState(WindowState.MAXIMIZED); writer.print(“
”); ... 因为 portlet mode 并不是定义来支持 Portlet 的, 且 user 是不能使 用 portlet mode 的, 所以不能用 portlet mdoe 来产生 portlet URL。 若执 意如此使用的话, 在这种状况下 setPortletMode 方法必须丟出 PortletModeException 异常。 当 portlet URL 的一个请求被调用时, portlet mode 的改变必须做出有效地回应。但还是有一些例外的状况, 比如说改变存取 控制权就有可能让 portlet mode 改变 不会发生。 Portlet container 并不支持 winodw state, 所以 portlet 不能运用 window state 来产生 portlet URL 。 假如执意如此使用的话, 此时 setWindowState 必须丟出 WindowStateException 异常。Window state 的改变 必须有效的反应一个 portlet URL 的请求。 门户系统 Portlet 开发技术解决方案 2.2.3.13.2. Portlet URL 安全 PortletURL 接口的 setSecure 方法允许 portlet 来定义 portlet URL 是 否必须为一个安全的 URL(即 HTTPs 或 HTTP)。 假若没有运用 setSecure 方 法, portlet URL 必须与当前的请求是同样的安全等级(也就是维持请求时的安 全等级)。 2.2.3.14. Portlet 模式 Portlet mode 指出 portlet 正处于什么模式, Portlet 通常会根据所处 的模式而执行不同的工作并产生不同的内容。 Portlet 模式让 portlet 决定它该显示什么内容和执行什么动作。调用一 个 portlet 的时候,portlet container 会提供一个 portlet 模式给那个 portlet。当在处理一个请求动作时,portlet 的模式是可以用程序来改变的。 Portlet 规格定义了三个 portlet 模式: VIEW,EDIT,and HELP。 于是 PortletMode 类就为这三个模式定义了常数。 同时 Portal 是可以根据使用者的角色, 来决定是要提供(显示)哪几个 portlet 模式给使用者操作。 例如,匿名使用者可以操作 VIEW 和 HELP 等 portlet 模式的内容, 而只 有授权过的使用者可以操作 EDIT 这个 portlet 模式所提供的内容或动作。 2.2.3.14.1. VIEW Portlet 模式 在 VIEW 这个 portlet 模式里,所被期望要提供的功能是产生 markup 语 言来表现此时 portlet 的状态。 举例来说, portlet 的 VIEW 模式可以包含 一个或多个画面让使用者可以浏览与互动, 或是一些不需要 与使用者互动的静态内容。 门户系统 Portlet 开发技术解决方案 Portlet 开发者应该重写 doView 这个被定义在 GenericPortlet 类別里 的方法,来执行 VIEW 模式的功能。所有的 Portlet 都必须支持 VIEW 模式。 2.2.3.14.2. Portlet 编辑模式 在 EDIT 这个 portlet 模式里, protlet 需要提供内容和逻辑来让使用者 定制 portlet 的行为。portlet 的 VIEW 模式可以包含一个或多个画面让使用者可以浏览并输入一些定制的资料。 典型的说, EDIT 模式的 portlet 会设定或更新 portlet 的参数设定值。 参考 Portlet Preferences Chapter 可以得到更多有关 portlet 参数设定 的信息。 Portlet 开发者应该重写 doEdit 这个被定义在 GenericPortlet 类別里 的方法,来执行 EDIT 模式的功能。所有的 portlet 并不需要都提供 EDIT 这 个模式。 2.2.3.14.3. HELP Portlet 模式 在 HELP 这个模式里,portlet 应该提供有关这个 portlet 的 help 讯息。这 个 help 讯息可以是有关这个 portlet 的简单且条理清楚的视窗说明或是详细 的说明整个来龙去脉。 Portlet 开发者应该重写 doHelp 这个被定义在 GenericPortlet 类別里 的方法,来执行 HELP 模式的功能。 所有的 portlet 并不需要都提供 HELP 这个模式. 2.2.3.14.4. 自定义 Portlet 模式 为了特別的功能,Portlet 提供者可以定义一些定制的 portlet 模式,所 有的 portlet 可以使用 portal 所定义的 portlet 模式。 Portlets 必须用 这个 tag 在部署描述文件中定义其所要使用的定制的 portlet 模式。 在部署阶段, 这些在部署描述文件中定义的定制的 portlet 模 门户系统 Portlet 开发技术解决方案 式必须对应到 portal 所支持的模式。 假若没有这样做的话, portlets 将不会在该 portlet 模式里被调用。 举个例子, 一个支持 clipboard 和 config 这两个定制的 portlet 模式 的 portlet, 必须在部署描述档里写下这些设定: ... Creates content for Cut and Paste clipboard Provides administration functions config ... 在 Extended Portlet Modes 里定义了一连串的 portlet 模式的名称,和 可以利用在哪些方面的建议。 在执行的时候, 若有在部署描述文件中定义到这几个预先定义的定制化的 portlet 模式的名字时 Portals 可以自动把这几个定义给 mapping 起来。 门户系统 Portlet 开发技术解决方案 2.2.3.14.5. GenericPortlet Render 处理 GenericPortlet 类別在执行 render 方法时, 会根据在 doDispatch 方法 里所指定的 portlet 模式, 而把请求分配给 doView, doEdit,doHelp 这几 个方法。 portlet 可以重载 GenericPortlet 的 doDispatch 这个方法来达到将请 求分配给另外定制的 portlet 模式。 2.2.3.14.6. 定义 Portlet 模式支持 在部署描述文件中里, Portlets 必须定义其对任何 markup 语言所支持的 portlet 模式。 正因为所有的 portlet 都必须支持 VIEW 模式, 所以 VIEW 模 式是不需定义的。 假如某个 portlet 模式并没有被定义来支持某个 markup 形态的时候, 则 portlet 在该 portlet 模式里是不能被驱动的。 下面的例子显示 在 部署描述档里定义一个 portlet 模式的片段语法: ... text/html edit help ... 门户系统 Portlet 开发技术解决方案 text/vnd.wap.wml help ... ... 对 HTML 的 markup 来说, portlet 支持 EDIT 和 HELP 和一定要的 VIEW 这三种 portlet 模式。 就 WML markup 来说,portlet 仅支持 VIEW 和 HELP 两种 portlet 模式。 至于 portal 在执行上没有支持的的其他定制的 portlet 模式, 或是 portal 不支持的 portlet 模式, Portlet container 都必须忽 略 2.2.3.14.7. 窗口状态 一个 portlet 可以根据视窗状态来决定在一个页面里该占多少大小。 当调 用一个 portlet 时, portlet-container 需要告诉该 portlet 目前的视窗状 态。 此时 portlet 可以根据视窗状态来决定它该对多少信息来作处理。 在处 理请求的过程中, portlet 可以通过程序的方式来改变视窗状态。 Portlet 规格里定义了三个视窗状态: NORMAL, MAXMIZED, MINIMIZED。 WindowState 这个类定义了这三个状态的常量值。 门户系统 Portlet 开发技术解决方案 2.2.3.14.7.1. 普通窗口状态 NORMAL 表示: • portlet 可以与其他 portlet 一起共用一个页面。 • 读取的此页面的客户端设备(ex: PDA, 手机, etc.) 有受限的显示功能。 因此在这此视窗状态下,portlet 必须限制其本身画面可以显示的大小, 以符合读取设备的显示功能。 2.2.3.14.7.2. 最大化的窗口状态 MAXIMIZED 表示: • portlet 可以把整个画面都占掉, 也就是说在整个 portal 页面里只有 一个 portlet。 • 或是相对地来说,整个页面里就属这一个 portlet 占了最大的版面。 • 当然, 在此状态下,portlet 可以产生更丰富的资料。 2.2.3.14.7.3. 最小化的窗口状态 MINIMIZED 表示: • 在此状态下, portlet 必须处理产生最小化的资料, 亦或是都不输出任 何内容。 门户系统 Portlet 开发技术解决方案 2.2.3.14.7.4. 自定义窗口状态 Portal 的提供者可以定义定制的视窗状态。 Portlets 只能使用 portal 所定义的视窗状态。 也就是说 portlets 必 须在其部署描述文件中定义其可以使用的视窗状态。 在部署的时候, 部署描述 文件中所定义的定制化的视窗状态必须符合 portal 执行所支持的视窗状态。 否则, 在此状态下 portlet 是不会被调用的。 假设要做到 half_page 这个视窗状态时, 要如何定义,以下例子说明在 部署描述文件中该如何定义所支持的视窗状态: ... Occupies 50% of the portal page half_page ... ?’。 的 name 来做为 识別。 而 为 portlet windows 的识別, 为此 attribute 的 name。 Attribute 放在 PORTLET_SCOPE 并不是用来保护不被其他 web component 存取, 而仅是为了提共一个方便的 namespace 来使用。 PortletSession 的 setAttribute 此 method 是用来系结一个 object 到 一个指定 scope 的 session。 门户系统 Portlet 开发技术解决方案 PortletSession session = request.getSession(true); URL url = new URL("http://www.foo.com"); 25 session.setAttribute("home.url" , url , PortletSession.APPLICATION_SCOPE); session.setAttribute("bkg.color" , "RED" , PortletSession.PORTLET_SCOPE); PortletSession 的 getAttribute 此 method 是用来取得存放在一个 session 中的某个 attribute。 PortletSession 的 removeAttribute 此 method 是用来移除存放在一个 session 中的某个 attribute。 而若要知道物件什么时候从 session 中放入或移除,可以通过执行 HttpSessionBindingListener 来达到目的, 相关内容在 Servlet Specification 有详述。 另外 PortletSessionUtil 提供一些 method 来分辨 object 是存放在哪 一个 scope 中的。 门户系统 Portlet 开发技术解决方案 2.3.4.4. Session与Web Application HttpSession之间的关系 Portlet Applciation 本身也是一个 Web Application. 所以 Portlet Application 中也有可能会有 Servlet 或是 jsp。 Porltet, servlet, jsp 可以通过 session 来分享信息。 PortletSession 存放的资料必须可以通过 HttpSession 取得, 反之亦然。 而当某个 HttpSession 被注销掉 (invalidated), 对应的 PortletSession 也 会被注销掉, 反之亦然。 2.3.4.5. Session与HttpSession Method的对应 以下几个 PortletSession 的 method 必须以 HttpSession 同名的 method 为基础: getCreationTime , getId , getLastAccessedTime , getMaxInactiveInterval, invalidate, isNew, setMaxInactiveInterval。 其中 getAttribute , setAttribute , removeAttribute , getAttributeNames 必须遵守以下一些原则 : • 如果使用 APPLICAITON_SCOPE, 则 attribute names 必须相同。 • 如果使用 PORTLET_SCOPE, 则必须遵守特殊的 prefix。 • 若使用没有指定 scope, 则等同于使用 PORTLET_SCOPE 门户系统 Portlet 开发技术解决方案 2.3.4.6. Session保留的HttpSession Attribute Names 以 "javax.portlet 。 " 开头的 session attribute names 是保留给 Portlet Specification 以及执行 portlet container 厂商所使用的。 Portlet container 的厂商可以使用此 namespace 来来存放执行特有的元件, 因此 application developer 不可使用用此 namespace 当做 attribute name 的开头。 2.3.4.7. 会话超时 跟 Servlet Specification 2.3, SRV.7.5 Section 相同。 2.3.4.8. 上次访问时间(Last Accessed Times) 跟 Servlet Specification 2.3, SRV.7.6 Section 相同. 2.3.4.9. 重要的会话语义(Important Session Semantics) 跟 Servlet Specification 2.3, SRV.7.7.3 Section 相同. 门户系统 Portlet 开发技术解决方案 2.3.5. 请求(Request) 2.3.5.1. 请求参数(Request Parameters) 假若 portlet 收到一个 client 对其发出的请求的时候, 其中的请求参数 必须是被 encode 在 URL 字符串里(在产生 PortetURL 的时候已经处理好了), 而且是对该 portlet 发出的请求。这些请求参数经由 "x-www-formurlencoded" 的方式被 decode 回 name value pairs。(在 PortletURL 接口里定义了 serParameter(s) 方法就是用"x-www-formurlencoded" 的方式来 encode 参数 的 name 和 value。) Portlet-container 不能把在 action request 得到的参数传递到后续的 render request。 假如 portlet 要这样做, 可以通过 render URL 或是在 processAction 的时候,利用 ActionResponse 物件的 setRenderParameter or serRenderParameters 方法来做。 假如在 portal page 里的一个 portlet 收到一个是要给其他 portlet 的 render request 时, 这个 request 的参数必须与前一个 render reuqest 的 参数一样。 假如 portlet 在同一个 client request 里收到一个跟在 action request 后的 render request, 这时的参数必须与在 action request 时的一样。 一般来说, portals 可以控制 portlet 的 portlet mode 和 window state。 在操作的这些 URLs 是由 portal 所产生的。 Client 经由 触发 URLs 来对 portlet 发出的请求, 必须被视作是 render URLs, 而且所发出的请求 参数是必须被保存的。 门户系统 Portlet 开发技术解决方案 一个 portlet 不能收到要给別的 portlet 的请求参数。 参数是以 name-value pairs 的方式被储存着。 重复的参数也可已被储存。 可以通过以下 PortletRequest 接口所提供的方法来取得参数资料。 • getParameter • getParameterNames • getParameterValues • getParameterMap getParameterValues 方法回传一个 String 物件的阵列包含全部相同名字的 参数的值。 用 getParameter 方法所得到的值, 是在这些同名参数里的第一个 的值, 也就是用 getParameterValues 回传的阵列里的的第一个的值。 假如只 有一个参数符合 getParameterValues(java.lang.String name) 所用的 name, 则必须回传一个 size=1 的 String 阵列。 getParameterMap 方法必须回传一 个不能被修改的 Map 物件。 假如 request 没有任何参数, 则 getParameterMap 必须回传一个空的 Map 物件(non null)。 这 Map 物件里的 key 是 type String , 而 value 是 String array(String[])。 额外的请求参数 Portal/portlet-container 执行时可能在 portlet URLs 里加一些额外的 参数来做整体的 routing 和 处理 client requests 。 Portal/portlet-container 所用的额外参数必须不被正在接收请求的 portlet 所见。如果 portal/portlet-container 用的参数的命名有可能与 portlet 所 定义的参数冲突, 这些问题必须由 portal/portlet-container 负责避免。 当然 Portlet Spec. 就有规定一些名字是保留字: “javax.portlet.”。 门户系统 Portlet 开发技术解决方案 2.3.5.2. 请求属性(Request Attributes) Request attributes 是在一个独立的 portlet request 里的物件。 Portlet 或是 portlet container 可以设定 request attributes 来表示一些 无法通过 API 表示的信息。 通过 request attributes 可以与经由 PortletRequestDispatcher 所 include 的 servlet or JSP 来共用信息。 Attributes 可以经由下述 PortletRequest 接口所提供的方法来设定, 取 得和移除: • getAttribute • getAttributeNames • setAttribute • removeAttribute 不像 parameter, 一个 attribute name 只有一个 value。 同样的 attribute 的命名也不能是 “javax.portlet.” 开头的字。 建议依据 Java Programming Language Sepcification 1 所提的 naming convention 来做 attribute 的命名。 Portlet 可以得到 portal/portlet-container 的属性, 假如可已的话, 连 HTTP client request 的 headers 信息都可以通过下列 PortletRequest 接 口提供的方法得到. • getProperty • getProperties • getPropertyNames 这里可能有同名的属性值。 同名的属性值在用 getProperty 方法取得时,会回 传第一个的值。 用 getProperties(String name) 方法可以回传一个 Enumeration object 包含这个 name 的全部属性值 门户系统 Portlet 开发技术解决方案 (of type String)。 依靠底层不同的 web-server/servlet-container 和 portal/portlet-container 所执行的, client request HTTP headers 也许不 一定都可以正确取得。 所以 portlets 不行依靠 headers 的信息来动作。 PortletRequest 接口有提供一些特定的方法来取得一些常见的 Header 信息: content-length,content-type, accept-language。 虽然 portal/portlet-container 在执行上可能会用其他方法来决定这 些 header 信息,不过 portlet 仍可利用这些特定的方法来取的这些信息。 2.3.5.3. 请求上下文路径(Request Context Path) 在 request 物件里就可以看到 context path。Context path 是所部署的 portlet application 的前缀字。 假若 portlet application 在 web server URL namespace 里是 root 的 话(也就是 "default" context), 此时的 context path 必是一个空字串。 否 则 context path 就是这个 portlet application 的 root 路径。 这个 path 必须是"/"开头,且结尾不能是"/"。 2.3.5.4. 请求对象的生存期 每一个 request 物件只有在一个特定的 processAction or render 方法里 时是有用的。 Containers 通常会循环再利用 request 物件以避免在每次产生 request 物件时, 造成效能上的负担。 开发人员应该注意到若在 processAction 或 render 方法这两个 scope 之外来维护参照 request 物件 时, 可能会造成 non-determisnistic 的反应。 门户系统 Portlet 开发技术解决方案 2.3.5.5. Portlet 请求(Portlet Request) Request 物件封装了所有有关 client 的请求, 参数, 请求内容, portlet mode, portlet state 等等的信息。 Portlet 的 request 物件是交由 processAction 和 render method 来 处理。 2.3.6. 响应(Response) 2.3.6.1. 响应属性 Portlets 可以利用 Properties 将 portlet 制造厂商定义的信息传给 portal/portlet-container。 portlet 可以通过以下 PortletResponse 接口所提供的方法来设定 properties • setProperty • addProperty setProperty 方法分別传入参数 name 和 value 来设定 property 。而之 前设定的 property 将会被新的 property 所取代。如果此 name 含有多个 property values,则同样所有 property values 将会被清空,而被新的 property value 所取代。 addProperty 则是新增一个 property value 到要求 的 name 的集合中, 而不会清除掉之前已存在的 property 。如果此 name 没 有任何相关的 property values ,则新增一个集合。 门户系统 Portlet 开发技术解决方案 2.3.6.2. Portlet 响应(Portlet Response) Response 物件在 client 请求的过程中封装了所有从 portlet 回传给 portlet container 的信息,如 redirection, portlet mode,title 和内容 等等。而 portal/portlet container 利用这些信息产生 response 并回传给(通 常为 portal page ) client 端。 Portlet 的 response 物件是交由 processAction 和 render method 来处理。 2.3.6.3. 响应对象的生存期 一个 response 物件只有在一个特定的 processAction or render 方法里 时是有用的。 Containers 通常会循环再利用 response 物件以避免在每次产生 response 物件时, 造成效能上的负担。 开发人员应该注意到若在 processAction or render 方法这两个 scope 之外来维护参照 response 物件 时, 可能会造成 non-determisnistic 的反应。 门户系统 Portlet 开发技术解决方案 2.3.7. 上下文(Context) 2.3.7.1. Portlet 上下文(Portlet Context) PortletContext 接口定义了一个 portlet 在 portlet application 里执 行时的概览。 也就是说, 通过 PortletContext 物件, portlet 可以记录事 件, 或取得 portlet application 的资源, 设定和储存一些其他 portlet 和 servlet 可以取得的属性值。 2.3.7.2. Portlet 上下文的作用域(Scope of the Portlet Context) 任何部署在 portlet container 里的 portlet application 都有一个 PortletContext 实例。 假使 container 是分散在不同的 VM 上, 一个 portlet application 在每个 VM 上都会有一个 PortletContext 实例。 2.3.7.3. Portlet 上下文功能(Portlet Context functionality) 通过 PortletContext 接口, 我们可以得知 context 的起始设定参数,并 获得和储存 context 属性, 从 portlet application 取得静态的资源, 和取 得一个 request dispatcher 来作 include servlets 和 JSPs 的动作。 门户系统 Portlet 开发技术解决方案 2.3.7.4. Portlet 上下文功能与 Servlet 上下文的关系 一个 portlet application 是一个 web application 的延伸。 就延伸自一个 web application 而言, portlet 也有一个 servlet context。 同时 portlet context 支持大多数的 portlet application 的 servlet context 的功能。 Context 层级的初始参数就如同 servlet context 的初始参数, 同时 context 属性值是存在于 servlet context 中, 且是可以互相分享的。 因此, 这些初始参数是必须定义在部署描述文件中的(web.xml 文件) 。 用 PortletContext 来取得的初始参数值,就如同用 portlet application 的 servlet context 来取得是一样的。 用 PortletContext 提供的方法来储存 context 的属性值, 这些值必须 储存在 portlet application 的 ServletContext 里。 这样做的直接的影响是: 用 servlets 或 JSPs 来存在 ServletContext 里的资料, 都可以通过 PortletContext 来取得, 反之亦然。 • PortletContext 必须提供方法来取得 ServletContext 所公开的资源。 • PortletContext 必须处理 ServletContext 处理的缓存目录。 而 PorteltContext 可以把这个缓存目录的信息当作是 context 属性值一 样, 通过 Servlet 2.3 规格第 SVR 3 章所定义的常量 (javax.servlet.context.tempdir) 来取得。 • 在 virtual hosting 和 reloading considerations 这方面, portlet context 必须遵照 servlet context 所定义的行为与功能。(参考 Servlet Specification 2.3 SVR 3 Servlet Context Chapter) 门户系统 Portlet 开发技术解决方案 2.3.7.5. ServletContext 和 PortletContext 方法之间的通信 以下的方法为 ServletContext 所提供的, 且 PortletContext 必须提 供相同的功能: • getAttribute • getMimeType • getRealPath • getResource • getResourcePaths • getResourceAsStream • log • removeAttribute • setAttribute 2.3.7.6. 门户的上下文(Portal Context) PortalContext 是一个 interface 让 portlet 可以去取得来自 portal 的一些讯息而 portlet 只能读取 portal 的信息, 不能去修改他 目前提供了以下几个 method。 • getPortalInfo() o 回传一些 Portal 的信息, 例如 Portal 的制造厂商及版本, 如果是 我们就要回传 jCharon 1。0 • getProperty(java。lang。String name) o 依照给的属性名称回传 Portal 的 Properties 设定值, 如果没有该 属性则回传 null。 • getPropertyNames() o 回传 Portal 所有的 Properties 设定值, 传回物件是 Enumeration • getSupportedPortletModes() 门户系统 Portlet 开发技术解决方案 o 回传 Portal 支持哪些 portlet mode, 传回物件是 Enumeration . • getSupportedWindowStates() o 回传 Portal 支持哪些 window state, 传回物件是 Enumeration . 一个 portlet 包含一个 PortalContext 的物件使用 portlet request 物 件的 getPortalContext() 取得该物件 2.3.8. 缓存(Caching) Caching content 将减少 Portal 回应给使用者的时间。 他也减低了 server 的负载量。 Portlet 规格书定义了基于 caching 机制的一个过期 ( expiration ) 的时间值。 Caching 机制是属于每个使用者每个 portlet。 Cached content 不允许分享相同 portlet 显示给予不同的使用者。 Portlet container 不需要去执行 expiration caching。 Portlet containers 执行这 caching 机制可能去取消他, 为了内存资源在任何时间可以作部分取消或全部 取消。 门户系统 Portlet 开发技术解决方案 2.3.8.1. 过期缓存 Portlets 希望他们的内容可以被 cached 使用 expirtation cache 必须定 义 expiration cache 的期间(秒) 在 deployment descriptor。 以下是一个定义 5 分钟(300 秒)的范例 : ... ... 300 ... 在 portlet 定义的 expiration cache 可以通过程序化地修改 expiration 时间, 只要使用在 PortletResponse 接口中 RenderResponse , 调整 EXPIRATION_CACHE 这个参数。如果 expiration 属性值设为 0, 这个 portlet 的 caching 将被取消。 而 expiration 的属性值设为 -1, 则这个 cache 将永远存在。 假如一个 render 的呼叫 expiration cache 属性却没有 设定, 那么 expiration 的时间将会根据 deployment descriptor 中的定义。 为了没有在 deployment descriptor 定义 expiration cache 参数的 portlet, 假如又去设定他的 expiration cache, portlet container 必须去忽视他。 门户系统 Portlet 开发技术解决方案 假如 portlet content 被 cahced, 那么 cache 将不会 expired 而且那 portlet 不是使用者 request 的目标, 所以 portlet 的 request 处理方式不 应该呼叫成为 client request 的一部份。 换句话说, portlet container 就 应该要由 cache 取出资料。 假使 portlet content 是被 cached, 但是 client request 的目标是相同的 portlet, 则 portlet-container 应该不要 理会 cache 的值, 必须重新呼叫 request 要求执行 portlet 中的 method。 2.3.9. 模式(Model) 2.3.9.1. Portlet 模式 PortletRequest 接口提供的 getPortletMode 可以让 portlet 取得目前 的的 portlet mode。 可以限制 portlet 提供某种 portal/portlet-container 所支持的 portlet mode。 Portlet 可以利用 PortletRequest 接口提供的 isPortletModeAllowed 来得知此 portlet 是否可以使用某个 portlet mode。 若 portlet mode 并未被该 portlet 所定义, 或是 portal 限制了该种 portlet mode, 则该 portlet 就不能使用该 portlet mode。 窗口状态 PortletRequest 接口提供的 getWindowState 让一个 portlet 得知其目 前的 window state。 一个 portlet 可以被限制只能使用某种 portal/portlet-container 所支 持的 window state。 一个 portlet 可以利用 PortletRequest 接口提供的 isWindowStateAllowed 方法来得知本身是否可以使用该 window state。 门户系统 Portlet 开发技术解决方案 2.3.10. 应用程序(Application) 2.3.10.1. Portlet 应用程序 一个 portlet application 就是一个 web application, 就如同定义在 Servlet Specification 2.3, SRV.9 章节, 包含了 portlets 及一个 portlet deploytment descriptor ( portlet.xml ) 附加一些 servlets, JSPs, HTML pages, classes 及 other resources 都会在一般的 web application 看到相 似的东西。 一个包装好的 portlet application 可以在执行多任务的 portlet containers 中执行。 与 Web 应用程序的关系 所有的 portlet application 元件和资源文件( resources ) 除了 portlets, 是被 servlet container 所管理, portlet container 也是建 置在 servlet container 之上。 PortletContext 关系 portlet container 必须强制 portlet application 和 PortletContext 是一对一的对应关系。 假如这个 application 是分布式的 application, portlet container 必须对每个 VM 建立一个 instance。 PortletContext object 将提供给 portlet application 一个具有 view 端呈现的 portlet。 门户系统 Portlet 开发技术解决方案 Portlet 应用程序的元素 Portlet application 可能包含了一些 portlets 加上其他可以放在 web application 的元素, 例如 servlet, JSP pages, classes, 静态的文件。 除了 web application 规定的 meta 信息, portlet application 必须包含 一个 descriptive meta information 去描述 portlet 所包含的东西。 目录结构 Portlet application 允许和 web application 具有相同的目录结构。 除 此之外, 他必须包含 /WEB-INF/portlet。xml 这个 deployment descriptor 档案。 Portlet classes, utility classes 及其他 resources accessed 通 过 portlet application classloader 必须摆放在 /WEB-INF/classes 目录或 包装在 /WEB-INF/lib/ 目录下的 JAR 档案。 门户系统 Portlet 开发技术解决方案 Portlet 应用程序 Classloader Portlet container 必须和 servlet container 给 web application 使用 的相同的 classloader 去载入 portlets 及 portlet application 相关的资 源 ( resources ) , portlet container 必须确认定义在 Servlet Specification 2.3 SRV.9.7.1 and SRV.9.7.2 章节中的需求。 Portlet 应用程序存档文件 Portlet applications 是被包装成 Web Application Archives (WAR), 可 以查看 Servlet Specification 2.3 SRV.9.6 章节。 Portlet 应用程序部署描述符 除了 web application 有个 deployment descriptor ( web.xml ) , Portlet application 也有一个 portlet application deployment descriptor (portlet.xml)。 portlet deployment descriptor 包含了一些 portlets 的设 定信息。 可以参考 Packaging and Deployment Descriptor 这个章节会有详细的说 明。 门户系统 Portlet 开发技术解决方案 替换一个 Portlet 应用程序 Portlet container 应该能够置换新版本的 portlet application , 而不 用重新启动 container。 此外, 在置换 porlet application 的同时, portlet container 应该提供一个穩定安全的方法去存放在 portlet application 之中 的 session data 。 错误处理 这是留给执行 portal/portlet-container 如何作出当 portlet 接到一个 request 后丟出一个 exception。 例如 portal/portlet-container 可以产生 一个错误页面去代替原本的 portal 页面, 或是产生一个错误讯息在 portlet window, 甚至可以从 portal 页面中移除该 portlet 只要记录 log 讯息提供 给 administrator 查询。 Portlet 应用程序环境 Portlet Specification 利用了许多 Servlet Specification 2.3 SRV.9.11 制定好的规格。 门户系统 Portlet 开发技术解决方案 2.3.11. 内容类型(Content Types) 2.3.11.1. 响应内容类型(Response Content Types) Portlet developer 可以写一个支持多重 content type 的 portlet。 Portlet 可以利用 request 物件的 getResponseContentType 方法来取得 container 所认为该 output 所代表的的一个字符串模式的 content type。 在 portlet 的 output 上, 若是 container 有支持其他的 content types, 则 必须利用 request 物件的 getResponseContentTypes 来得知。 此方法回传一 个包含所有 container 所支持的 content tpye 的 Enumeration 物件。 这里 面的第一个 content type 必须跟 getResponseContentType 所回传的一样。 假如 portlet 用万用字符 '*' or ' / ' 来定义支持所有的 content type, 且 container 支持全部的 content type, 则 getResponseContentType 可以 回传万用字符 '*' or ' / *' 或是 container 所 prefer 的 content type。 getResponseContentTypes 方法必须是回传该 portlet mode 所支持的 content types。 门户系统 Portlet 开发技术解决方案 2.3.11.2. Portlet内容类型(Content Type) Portlet 必须使用 RenderResponse 接口的 setContentType 方法设定 response 的 content type 当 content type set 和从 PortletRequest 物件的 getResponseContentType 回传的 content types 不相符,则 setContentType 会丟出 IllegalArgumentException。 portlet container 应该忽略那些指定成部分 content type 的任意 character encoding 。 如果 getWriter 或 getPortletOutputStream 方法在 setContentType 之 前被呼叫,则必须丟出 IllegalStateException。 setContentType 方法必须在 getWriter 或 getPortletOutputStream 方法前被呼叫。如果在之后被呼叫,则不发挥作用。 如果 portlet 已经设定好了 content type ,那么 getContentType 必 须回传他。相反的如果没有设,则 getContentType 方法会回传 null 。 检索上传数据 当 client request 包含了 HTTP POST 资料为非 "application/x-www-form-urlencoded" 的形态时, 这个 input stream 是非 常有用的。 比如, 上传档案。就 Portlet developer 的方便性来说, ActionRequest 接口同时还提供 getReader 方法来取得 HTTP POST character 格式的资料。 在一个 action request 里, 只有 getPortletInputStream 或 getReader 其中一个可以被使用。 假如已经呼叫 getPortletInputStream, 则又呼叫 getReader 时, 就必须丟出 IllegalStateException, 反之亦然。 门户系统 Portlet 开发技术解决方案 为了帮助管理 input stream,ActionRequest 接口也提供下列方法: • getContentType • getCharacterEncoding • setCharacterEncoding • getContentLength setCharacterEncoding 只有设定 character set 给 getReader 方法所回传的 Reader 用。 假如 user 请求的 HTTP POST data 是 application/x-www-form-urlencoded 的格式, 则这个资料必须是已经被 portal/portlet-container 所处理过, 且 可以在 request parameters 里被取得。 此时若呼叫 getPortletInputStream or getReader 方法时都必须丟出 IllegalStateException. 输出流和编写器对象 Portlet 可以通过写到 RenderResponse 物件的 OutputStream 或者是 Writer 来产生内容。 portlet 只可以用其中一种 object 。如果 portlet 尝 试使用两者,则 portlet container 必须丟出 IllegalStateException。 缓存 Portlet container 允许(但不是必须)为了效率问题,将输出到 client 的 output buffer 起来。一般来说 servers 会将 buffering 设为 default ,但 是允许 portlets 去指定 buffering parameters。 RenderResponse 接口提供的以下方法,允许 portlet 去操作和设定 buffering 信息。 • getBufferSize • setBufferSize 门户系统 Portlet 开发技术解决方案 • isCommitted • reset • resetBuffer • flushBuffer 以上方法都由 RenderResponse 接口所提供,允许当 portlet 在使用 OutputStream 或 Writer 时运行 buffering operations。 getBufferSize 方法回传底层已经使用的 buffer 大小。如果没有任何 buffering 被使用,则回传 int 0。 Portlet 可以使用 setBufferSize 方法来要求选择的 buffer 大小。 assigned 的 buffer 大小并不一定得和 portlet 要求的大小相同,但是至少必 须等于所要求的大小。这可以使 container 重复使用固定大小的 buffers ,提 供大于要求的合适大小。此方法必须在任何内容使用 OutputStream 或 Writer 写出之前呼叫。如果已经有内容被写出,则此方法会丟出 IllegalStateException 。 isCommitted 方法回传 boolean 值,表示所有回传资料是否已回传到 client 端。 flushBuffer 方法则是将所有在 buffer 内的内容传到 client 端。 Reset 方法是当 response 并未 commit 时清空 buffer 内的资料,且在呼 叫 reset 方法之前 portlet 设定的 Properties set 必须被清除。 resetBuffer 是当 response 并未 commit 时清除 buffer 内的资料。但是并不 清除 properties 。 如果 response 被 commit 之后呼叫了 reset 或是 resetBuffer 方法 ,则 必须丟出 IllegalStateException ,而 response 跟其相关的 buffer 不能被 更动。。 当使用 buffer 时, container 必须将塞满 buffer 的内容 flush 到 client 。如果这是第一次将资料送到 client ,则 response 必须被视为已经 commit 了。 门户系统 Portlet 开发技术解决方案 命名空间编码 在 content 中, portlet 包含的 elements 必须在整个 portal page 中 是唯一的值, JavaScript function 和变量就是其中一种例子。 getNamespace 必须提供 portlet 一个方法,以确保回传的字串在整个 portal page 中是唯一的。例如使用 getNamespace 可以取得一个特殊字串,可 以用来 prefix 在 portlet 产生的 content 中的 Javascript 变量名称的前 面,以确保它在整个 portal page 中是独一无二的。 getNamespace 必须回传由 Java Language Specification Second Edition 中 3。8 Identifier Section 所定义的合适的 Identifier。 Portlet 标题 Portlet 可以向 portal/portlet-container 提出所 preferred 的 title 。是否使用 portlet 所设定 的 preferred title ,则是 portal/portlet-container 的责任。 SetTitle 必须在 portlet 写出到 output 之前呼叫, 如果在之后呼叫, 则 setTitle 将不会产生任何作用。 国际化(Internationalization) Portal/portlet-container 决定要用什么 locale 来产生 response 给 User。 Portal/portlet-container 可以参考 client 请求的信息。 举例来说, HTTP/1.1 规格所定义的 header 里面的 Accept-Language。 门户系统 Portlet 开发技术解决方案 PortletRequest 的 getLocale 方法, 可以告知 portal/portlet-container 所选择使用的 locale。 Portlet标签库 portlet tag library 可以放在 JSPs 的档案之中去直接存取 portlet 特 殊的元素, 例如 RenderRequest 及 RenderResponse。他也提供了 JSPs 去存取 portlet 的一些功能如 portlet 的 URLs。 Portlet container 必须提供 portlet tag library 的执行。 Portlet 的 开发人员可以使用定义在 JSP Specification 1.2 中 JSP.7.3.9 Well-Know URIs 的机制来执行。 而 JSP 该如何使用 tab library 呢 ? 如同下面的程序码 <%@ taglib uri=”http://java.sun.com/portlet” prefix=”portlet” %> 定义对象标签 defineObjects tag 必须定义以下的变量让 JSPs 可以使用 • RenderRequest renderRequest • RenderResponse renderResponse • PortletConfig portletConfig 这些变量必须参照 Portlet API 的物件并且放在 request scope 的物件中 ( 定义在 Included Request Attributes 章节 ) 门户系统 Portlet 开发技术解决方案 一个 JSP 程序中可以通过 scriptlet 利用到 defineObjects tag 在所有 的地方。 defineObjects tag 不可以定义任何属性, 也不可以包含任何 body content。 简单的 JSP 范例如下 <%=renderResponse.setTitle("my portlet title")%> 这就是通过 defineObjects tag , JSP 可以呼叫 renderResponse 的 setTitle() 去设定 portlet 的标题。 门户系统 Portlet 开发技术解决方案 ActionURL 标签 portlet 中 actionURL 是为了建立一个 URL 可以链结到目前的 portlet 并且可以通过带有参数的 request 动作去驱动。 而 URL 的传递参数必须使用 param tag 放在 actionURL 的起始以及结束 的 tag 之间。 • windowState ( 形态: String, 非必要的) 当这个 link 被执行的时候, 这个 portlet windowState 应该要执行的动 作。 预先设定的 windowState 有 minimized, normal, 以及 maximized 。 假 如这些标准的视窗状态在这个 request 是非法的, 应该就要丟出 JspException。 非法的理由有可能是 portal 不支持这些状态, portlet 无法 宣告他们在 Deployment Descriptor 之中让 portal 支持这些状态, 或是现在 这个使用者不被允许使用到这些状态。 假如视窗状态不能让 URL 设定, portlet 将待在和现在这个 request 相同的状态。 WindowState 的属性应该是 不分大小写的。 • portletMode (形态: String, 非必要的) 假如当这个 link 被执行的时候没有错误发生, 这个 portlet 应该要具备 的 mode。 预先设定的 portlet mode 有 edit, help, 以及 view 。 假如这 些标准的 portlet mode 在这个 request 是非法的, 应该就要丟出 JspException。 非法的理由有可能是 portal 不支持这些 mode, portlet 无 法宣告他们在 Deployment Descriptor 之中让 portal 支持这些 mode, 或是 现在这个使用者不被允许切换到这些 mode 。 假如 portlet mode 不能让 URL 设定, portlet 将待在和现在这个 request 相同的 mode 。 PortletMode 的 属性应该是不分大小写的。 门户系统 Portlet 开发技术解决方案 • var (形态: String, 非必要的) 为了 action URL 输出到某个 scope 变量的名称。 这输出到 scope 的变 量名称应该是 String。 预设 URL 执行是把结果写到现在这个 JspWriter。 假 如结果输出成为 JSP 的 scope 变量, 通过 var 的属性定义, 将不会有任何 东西写到现在这个 JspWriter。 注意: 在 URL 被建立之后, 将不可能利用变量和 String 串联去 extend 那 URL 或增加任何更多的参数值。 假如被给定的参数名称已经存在在这个 page 的 scope 或他已经被使用在一个重复的迴圈中, 新的属性值将覆盖旧有的。 • secure (形态: String, 非必要的) 假如结果的 URL 必须是 secure 的链结 ( secure="true" ) 或一个 insecure one ( secure="false")。 假如 run-time 环境不支持标准的安全性 设定, 将会丟出 JspException。 假如这 URL 没有设定任何 security, 他必 须待在和现在 request 相同的 security 设定。 JspException 具有 PortletException 将产生一些错误视为 root 产生会 丟出以下的状况。 • If an illegal window state is specified in the windowState attribute。 • If an illegal portlet mode is specified in the portletMode attribute。 • If an illegal security setting is specified in the secure attribute。 以下是一个使用 actionURL tag 的例子 门户系统 Portlet 开发技术解决方案 这个范例是建立一个 URL 将让 portlet 进入 EDIT 的状态并且 window state 最大化 ( MAXIMIZED )的去编辑股票报价列表。 renderURL 标签 portlet 中 renderURL 是为了建立一个 URL 可以链结到目前的 portlet 并且可以通过带有参数的 request 动作去驱动。 而 URL 的传递参数必须使用 param tag 放在 renderURL 的起始以及结束 的 tag 之间。 • windowState ( 形态: String, 非必要的) 当这个 link 被执行的时候, 这个 portlet windowState 应该要执行的动 作。 预先设定的 windowState 有 minimized, normal, 以及 maximized 。 假 如这些标准的视窗状态在这个 request 是非法的, 应该就要丟出 JspException。 非法的理由有可能是 portal 不支持这些状态, portlet 无法 宣告他们在 Deployment Descriptor 之中让 portal 支持这些状态, 或是现在 这个使用者不被允许使用到这些状态。 假如视窗状态不能让 URL 设定, portlet 将待在和现在这个 request 相同的状态。 WindowState 的属性应该是 不分大小写的。 门户系统 Portlet 开发技术解决方案 • portletMode (形态: String, 非必要的) 假如当这个 link 被执行的时候没有错误发生, 这个 portlet 应该要具备 的 mode。 预先设定的 portlet mode 有 edit, help, 以及 view 。 假如这 些标准的 portlet mode 在这个 request 是非法的, 应该就要丟出 JspException。 非法的理由有可能是 portal 不支持这些 mode, portlet 无 法宣告他们在 Deployment Descriptor 之中让 portal 支持这些 mode, 或是 现在这个使用者不被允许切换到这些 mode 。 假如 portlet mode 不能让 URL 设定, portlet 将待在和现在这个 request 相同的 mode 。 PortletMode 的 属性应该是不分大小写的。 • var (形态: String, 非必要的) 为了 render URL 输出到某个 scope 变量的名称。 这输出到 scope 的变 量名称应该是 String。 预设 URL 执行是把结果写到现在这个 JspWriter。 假 如结果输出成为 JSP 的 scope 变量, 通过 var 的属性定义, 将不会有任何 东西写到现在这个 JspWriter。注 意 : 在 URL 被建立之后, 将不可能利用变量 和 String 串联去 extend 那 URL 或增加任何更多的参数值。 假如被给定的参 数名称已经存在在这个 page 的 scope 或他已经被使用在一个重复的迴圈中, 新的属性值将覆盖旧有的。 门户系统 Portlet 开发技术解决方案 • secure (形态: String, 非必要的) 假如结果的 URL 必须是 secure 的链结 ( secure="true" ) 或一个 insecure one ( secure="false")。 假如 run-time 环境不支持标准的安全性 设定, 将会丟出 JspException。 假如这 URL 没有设定任何 security, 他必 须待在和现在 request 相同的 security 设定。 JspException 具有 PortletException 将产生一些错误视为 root 产生会丟出以下的状况。 • If an illegal window state is specified in the windowState attribute。 • If an illegal portlet mode is specified in the portletMode attribute。 • If an illegal security setting is specified in the secure attribute。 以下是一个使用 renderURL tag 的例子 这个范例将产生一个 URL 去提供一个 link 可以去显示公司其他公司的股 票报价及改变 portlet mode 成为 VIEW 并且将 window state 改成 NORMAL。 1.26.4. namespace Tag 这个 tag 产生一个目前 portlet 中唯一的数值。这个 tag 应该被 named elements 使用在 portlet 的输出, 例如 javascript 的功能或变量。 而 门户系统 Portlet 开发技术解决方案 namespacing 就是确认给定的名称是这个 portlet 唯一的值, 是避免名称重复 影响了其他 portal 的 element 以及这一页的其他 portlets。 namespace tag 不允许任何 Body Content。 以下是一个使用 namespace tag 的例子 doFoo()”>Foo 这个范例预先设定 javascript function 加上 namescape + 'doFoo', 以 确认这个功能在 portal page 中是唯一的。 门户系统 Portlet 开发技术解决方案 param 标签 这个 tag 是定义一个参数值, 可能是放在 actionURL 或 renderURL 之中。 param Tag 不准包含任何 Body Content。 下面是 param Tag 一些必要的属性 • name (形态: String, 必要的) 增加在 URL 之中参数的属性名称。 假如 name 是空值 (NULL) 或 空白字串 ( empty ), 将不会执行任何动作。 • value (形态: String, 必要的) 增加在 URL 之中参数的属性值。 假如 value 是空值 (NULL) 他将视同空白字 串 (empty) 处理。 以下是一个使用 param tag 的例子 门户系统 Portlet 开发技术解决方案 标记片段 portlets 产生的 markup 片段将被结合在一份 portal page 里,因此 portlets 生成这些内容时必须遵守一些规则和限制。 以下所列不允许的 tags,是会影响到其他 portlet 产生的片段,甚至破坏 整个 portal page 结构的 tags。markup 片段里含有这些 tag 将使整个片段无 效。 若 portlets 产生的是 HTML 片段,不可使用以下 tags: base、body、 iframe、frame、frameset、head、head、html、title。 若产生的是 XHTML 和 XHTML-Basic 片段,不可使用以下 tags: base、 body、iframe、head、html、title。 HTML、 XHTML 和 XHTML-Basic 的标准不允许某些元素用在 head 之外的地 方,但有些浏览器允许这些元素用在其他地方。例如目前版本的 Internet Explorer 和 Netscape Navigator 都支持 style 用在文件的任何地方。 portlet 开发者必须对使用以下符合本段描述的元素作谨慎的决定:link、meta、 style。 门户系统 Portlet 开发技术解决方案 CSS样式 CSS 与 icon 的设计对于一个 Portal 来说就是更换 skin 的方式 jCharon 会 设计一个 Web Application, 暂时命名为 jCharonDesigner jCharonDesigner 的目标就是让 Designer 可以快速地编写合适的 css 及 icon 将他做成 zip 档案, 让工程师放到 skin 之中使用 不过, 该如何设计 CSS Style ? 我们就需要先了解 JSR 168 做了哪些规范 链结文字Links (Anchor) Spec 并没有对 tag 做特殊的 class 定义。 一般文字(Fonts) Style portlet-font 一般最常使用的文字 portlet-font-dim 比正常文字更淺色一點的文字 如果开发人员需要放大縮小该文字 则是自行使用
Important Information
Small and Dim
门户系统 Portlet 开发技术解决方案 讯息文字(Messages) 讯息文字通常会放在 paragraph (段落) 之中,往往是执行某个事件后产生的讯息文字 常常设定的 css 参数有 alignment (靠齐), borders(宽度), 及 background color(背景顏色) 等等属性 Style 解释 portlet-msg-status 目前状态的讯息, 例如 "80%" portlet-msg-info 辅助的信息讯息, 一般附加讯息, 例如 "关于 xxx 请 查阅 aaaa" portlet-msg-error 错误讯息, 例如 "系统发生 404 错误" portlet-msg-alert 警告讯息, 例如 "目前系统忙碌中, 请稍后链结" portlet-msg-success 成功讯息, 例如 "你已经轉帳成功" 区块(Sections) section (区块)可能是由 table 或 div 所组成的 常常设定的 css 参数有 alignment (靠齐), borders(宽度), 及 background color(背景顏色) 等等属性 Style 解释 portlet-section-header section 表头文字 portlet-section-body section 内文文字 portlet-section-alternate section 交换文字, 通常用在奇数偶数列互换时 候 门户系统 Portlet 开发技术解决方案 portlet-section-selected section 被选择到的文字, 通常用在 OnMouseOver 或要特別标示某一列时候 portlet-section-subheader section 次表头文字, 有时候会有两个表头, 例 如上方或左方都是表头的时候 portlet-section-footer section 表尾文字, 表单结束通常会有一些附加 文字 portlet-section-text 说明这个 section 到底在干麻的文字, 可能在这 个 table 的上方或右下角等等 表单(Forms) Style 解释 portlet-form-label form 所有元素(elements)预设的标签文字 portlet-form-input-field 使用者输入的内容文字 portlet-form-button 按钮上的文字 portlet-icon-label 圖示旁邊的标签文字 portlet-dlg-icon-label 标准圖示 ( 例如"确定", "取消") 旁邊的标签文 字 portlet-form-field-label 如 checkbox, radio 等等的标签文字 portlet-form-field 和 portlet-form-input-field 不同, 不是输入内 容 (input field), 而是如 checkbox 前面的那些 □ 或 ○ 门户系统 Portlet 开发技术解决方案 菜单(Menus) 选单, 大陆称为 "菜单" 可能会是左方的控制选单或 PopupMenu (浮动选单) 甚至是 TreeMenu (树 状选单), TabMenu (标签选单) 等等的样式 Style 解释 portlet-menu 一般的选单, 定义他的背景顏色, 间 距等等 portlet-menu-item 一般没有被选择到的选单元件 portlet-menu-item-selected 被选择到的选单元件 portlet-menu-item-hover 一般不是被选择到的选单元件, 当鼠标 移到上面的时候呈现样式 portlet-menu-item-hover-selected 被选择到的选单元件, 当鼠标移到上面 的时候呈现样式 portlet-menu-cascade-item 一般不是被选择到的选单元件, 但是具 有次选单的样式 portlet-menu-cascade-item-selected 被选择到的选单元件, 但是具有次选单 的样式 portlet-menu-description 选单的说明, 可能是在下方对这选单做 一些解释 portlet-menu-caption 选单的标题, 可能在上方对于这选单做 标题说明 门户系统 Portlet 开发技术解决方案 2.3.12. 安全(Security) 2.3.12.1. 安全属性(Security Attributes) PortletRequest 接口提供一组方法来提供有关 User 和 User 与 Portal 间的关系的 "Security" 信息。 方法如下: • getAuthType • getRemoteUser • getUserPrincipal • isUserInRole • isSecure getAuthType 方法, 指出了 User 与 Portal 之间所用的 authentication scheme。 此方法回传所定的(BASIC_AUTH, DIGEST_AUTH, CERT_AUTH and FORM_AUTH)或是由开发商所提供的 authentication type 的字符串。 假如 User 未被 authenticated, 则 getAuthType 未回传 null。 getRemoteUser 方法, 回传发出请求的 User 的 login name。 getUserPrincipal 方法, 回传一个包含认证过的 User 的名字的 java.security.Principal 物件。 sUserInRole 方法, 显示了是否一个认证过的 User 是否拥有特定的 role。 isSecure 方法, 则是检验一个 request 是否通过安全的协定来传递(ex: HTTPS)。 门户系统 Portlet 开发技术解决方案 2.3.12.2. Portlet应用安全(Security) 不同的 developer 做出不同的 Portlet applications, 但是可能都是由 同一个 deployer 来作部署的动作, 因此 developer 需要告诉 deployer 该如 何设定相关的 security。 介绍 一个 portlet application 包含了许多可以被存取的资源, 但是在公开的 环境中, security 的需求就相对重要。 portlet container 将通过 user 的 role 来决定是否给予 user 存取该 portlet。 portlet container 不需要处理 user 的 authetication ( 就是不需要检查登录检查机制 ) , 而 authetication 应该是在 servlet container 所处理的, 定义在 Servlet Spec 2.3 , SRV.12.1 Security。 附注: Servlet Specification 2.3 Security 的特性。 • Authentication(身份验证): 提供身份的验证。 • Access control for resources(资源的控制权): 资源仅仅提供给予特定 的族群使用。 • Data Integrity (资料的完整): 保证传送中的资料不会被修改。 • Confidentiality or Data Privacy(机密或资料隐私): 只有具权限的使 用者使用该资料。 在 Servlet security 的机制中, 提供了 3 个 method 来验证身份及权限 • getRemoteUser : 得到 form 传来的 j_username , 如果没有通过 authetication, 则回传 null 。 门户系统 Portlet 开发技术解决方案 • isUserInRole : http://jakarta.apache.org/tomcat/tomcat-4.1-doc/realm-howto.html http://jakarta.apache.org/tomcat/tomcat-4.1-doc/security-manager-howt o.html http://java.sun.com/webservices/docs/1.3/tutorial/doc/Security.html • getUserPrinciple : 回传 user java.security.Principle 资料, 如果没有通过 authetication, 回 传 null。 http://java.sun.com/j2se/1.4.2/docs/api/java/security/Principal.html http://java.sun.com/j2se/1.4.2/docs/guide/security/index.html 角色 和 Servlet 2.3 SVR12.4 定义的是一样的 门户系统 Portlet 开发技术解决方案 编程的安全 其运行时和 Servlet 一样也是提供相同的 methods getRemoteUser, isUserInRole, getUserPrinciple 而且也可以在 portlet 的 element 中增加 security-role-ref ... ... FOO manager ... ... 指定安全约束 Portlet 也有一些特殊的 elements 可以设定 。portlet collection 可以 设定哪些 portlets 是被保护的, 这些 portlets 给予哪些群组才可以使用。 门户系统 Portlet 开发技术解决方案 user data constaint 可以设定例如 SSL 来保证资料的完整性。 ... accountSummary ... ... Secure Portlets accountSummary CONFIDENTIAL ... 门户系统 Portlet 开发技术解决方案 繁衍安全 因为 Portlet 属于 J2EE 的其中一个部分, 当 portlet application 呼 叫 EJB 的时候 , user 的 security 身份应该由 portlet container 传递到 EJB container。 所以需要在 web.xml 设定相关的 security 机制, 才能让 EJB container 认得这份 security identity。 另外可能的方法就是让 portlet application 单独登录 EJB 来作程序开 发。 2.3.12.3. 用户信息(User Information) 通常来说, portlets 提供个人化的网页设定给予使用者建立一些 reuqest. 为了达到这个功效,他们必须取得使用者的一些属性例如使用者的姓名, email, 电话及地址。 Portlet container 提供一个机制去陈列出使用者的属性给予 portlets 使用。 门户系统 Portlet 开发技术解决方案 定义用户属性 portlet application 中的 deployment descriptor 必须定义 portlets 会使用到的 user 属性, 下面是一个定义的范例: User Given Name user.name.given User Last Name user.name.family User eMail user.home-info.online.email Company Organization user.business-info.postal.organization 门户系统 Portlet 开发技术解决方案 deployer 必须对应到 portlet application 的使用者属性, 去符合 runtime 环境的使用者属性。 在 runtime 环境中, portlet container 使 用这对应表去陈列使用者属性给予 portlet application 中的 portlets. runtime 环境中的使用者属性如果无法对应到 deploytment 中就不允许给予 portlet 取得。 可以参考 User Information Attribute Names 里面有属性列表。 门户系统 Portlet 开发技术解决方案 访问用户属性 Portlets 可以包含一个不可修改的使用者属性 Map Object, 目前这个 request 和使用者相关的一些属性。 这个 Map Object 可以通过 USER_INFO 解析定义在 PortletRequest interface 之中。 假如在 context 被没有认 证(un-authenticated)过的使用者执行 request , 呼叫 USER_INFO 常量的 getAttribute method 将会回传 NULL 。 假如这个使用者被认证过 ( authenticated ) 且没有可获得的任何使用者属性, Map 则是一个空(empty) 的 Map。 Map Object 中的 user attribute 必须包含一个 String 名称-数值 (name-value) pair 。 Map object 应该只有包含使用者属性对应到 deployment 。 简单的 portlet 解析使用者属性的程序应该如下: ... Map userInfo = (Map) request.getAttribute(PortletRequest.USER_INFO); String givenName = (userInfo!=null) ? (String) userInfo.get(“user.name.given”) : “”; 15 String lastName = (userInfo!=null)? (String) userInfo.get(“user.name.family”) : “”; ... 门户系统 Portlet 开发技术解决方案 用户信息的重要注意事项 Portlet 规格制作群担心使用者资料制定超过了这份规格书的范围。 目前 没有标准的 Java 标准去存取使用者资料, 也没有相关的 Java 标准正在定 义, 所以 Portlet 规格书将提供这个机制, 也让 Portlet API 不要被认 为是太多管闲事。 未来只要有相关的 Java 使用者信息的标准被制定,现在这 个机制将会失效 ( deprecated )。 2.3.13. 接口(Interface) 2.3.13.1. PortletRequest Interface PortletRequest 接口定义了一些一般的功能, 并且由 ActionRequest 和 RenderRequest 两个接口来继承。 2.3.13.2. PortletResponse Interface PortletResponse 接口定义了一些一般的功能, 并且由 ActionResponse 和 RenderResponse 两个接口来继承。 2.3.13.3. RenderRequest Interface 继承自 PortletRequest 接口的 RenderRequest 接口是用在 Portlet 接 口定义的 render method。且 RenderRequest 接口并未定义其他方法。 门户系统 Portlet 开发技术解决方案 2.3.13.4. RenderResponse Interface 继承自 PortletResponse 接口的 RenderResponse 接口是用在 Portlet 接口定义的 render method 。 且 RenderResponse 接口让 portlet 可以设定 title 跟所产生的内容。 2.3.13.5. ActionRequest Interface ActionRequest 接口继承了 PortletRequest 接口, 且此接口使用在 Portlet 接口里的 processAction 里。 除了 PortletRequest 接口所提供的功 能之外,ActionRequest 还提供可以取得 request 的 input stream 的方法。 2.3.13.6. ActionResponse Interface ActionResponse 接口继承自 PortalResponse 接口,且被使用在 Portlet 接口中的 processAction 方法中.此接口让 portlet 可以 redirect 使用者到 其他 URL , 设定 render parameters , 改变 portlet 的 window state 和 portlet mode 。 门户系统 Portlet 开发技术解决方案 3. 通用开发方案 3.1. 运行环境 3.1.1. 硬件环境 硬件 配置 CPU 1CPU、双核 3.00GHz 以上 内存 2G 以上 硬盘 1G 以上 3.1.2. 软件环境 软件 产品及版本 操作系统 Windows2000 Service Pack4/XP/NT JDK jdk-1_5_0_06-windows-i586/jdk1.6 门户系统(集 成 WEB 服务 器) liferay-portal-tomcat-5.5-5.1.1 门户系统脚本 liferay-portal-sql-5.1.1 部署服务器支 持 apache-ant-1.7.1-bin 代码编译器 jikes-1.22-1 数据库 mysql-essential-5.1.24-rc-win32 数据库管理软 件 navicat8_mysql_cs(Navicat 8.0.20 破解补丁) 门户系统 Portlet 开发技术解决方案 数据库连接 JDBC 驱动 mysql-connector-java-5.1.6-bin 开发 IDE MyEclipse_6.6.0_E3.3.2_\lomboz-all-in-one-R-3.3\ 通用开发 IDE 插件 com.sun.jsr168.portlet.plugin_1.1.0.jar(JSR168 规范) portlet-container-configurator.jar(JSR168 规范) Liferay 开发 Portlet 插件 liferay-ide-eclipse-1.0.2.jar(Liferay 开发专用) liferay-plugins-sdk-5.1.1 3.2. 配置步骤 3.2.1. 配置测试环境 实例中采用 Liferay 的 Portal 作为开发测试门户系统,详细说明请参考 《Liferay 门户系统技术手册.doc》 3.2.2. 安装配置开发环境 3.2.2.1. 准备工作  安装 JDK(以 JDK1.6 为例),安装路径为:c:\jdk1.6  配置环境变量: JAVA_HOME :c:\jdk1.6  配置环境变量: JIKES_HOME :c:\jikes  配置环境变量: ANT_HOME :c:\ant  将 c:\jikes\bin 下的 jikes.exe 复制到 c:\jdk1.6\bin 下 门户系统 Portlet 开发技术解决方案 3.2.2.2. Eclipse开发环境 此方法只能开发通用的 Portlet,关于其他的扩展功能建议采用定制门户系 统 Portlet 开发方法 3.2.2.2.1. 安装Eclipse3.3(lomboz)  将 lomboz-all-in-one-R-3.3-200710290621-win32.zip 直接解压到任意目 录下(以 D:\lomboz 为例  将 com.sun.jsr168.portlet.plugin_1.1.0.jar ( JSR168 规范) portlet-container-configurator.jar ( JSR168 规范)复制到 D:\lomboz\eclipse\plugins 中  在“cmd”中运行: 之后运行 Lomboz: 门户系统 Portlet 开发技术解决方案 门户系统 Portlet 开发技术解决方案 3.2.2.2.2. 配置Eclipse3.3(lomboz) 3.2.2.2.2.1. 配置ANT_HOME: 修改系统默认的 ANT_HOME: 门户系统 Portlet 开发技术解决方案 门户系统 Portlet 开发技术解决方案 修改 ANT_HOME 完毕后点击确定 3.2.2.3. My-Eclipse6.6 开发环境 此方法只能开发通用的 Portlet,关于其他的扩展功能建议采用定制门户系 统 Portlet 开发方法 3.2.2.3.1. 安装my-eclipse6.6  点击 安装 my-eclipse6.6 3.2.2.3.2. 配置MY-Eclipse6.6 3.2.2.3.2.1. 配置ANT_HOME: 修改系统默认的 ANT_HOME: 门户系统 Portlet 开发技术解决方案 门户系统 Portlet 开发技术解决方案 点击“确定”完成配置 3.2.2.3.2.1. 配置JVM 修改 my-eclipse6.6 自带的 JVM 采用系统的 JVM: 点击“add”选择系统的 JVM 路径 门户系统 Portlet 开发技术解决方案 点击“OK”完成配置 门户系统 Portlet 开发技术解决方案 3.3. 开发步骤 3.3.1. 在Eclipse3.3(lomboz)中开发 3.3.1.1. 建立Portlet工程  运行 lomboz 后新建工程选择: JSR168 工程,效果图如下: 门户系统 Portlet 开发技术解决方案  Portlet Name:Portlet 工程名称  Create Project In Workspace :在工作区中建立 Portlet 工程  Direcctor: 定向到源 Portlet 工程中 暂不支持 J2EE 版本选择 点击“finish” 门户系统 Portlet 开发技术解决方案 3.3.1.2. 工程结构图  SRC/java: JAVA 文件的源代码位置  Classes:编译后的 class 字节码文件存放位置  Lib:所需的 JAR 文件存放位置,主要用到的是:Portlet.jar  SRC: 源代码存放位置  Web:web 应用程序文件其具体结构如下: Classes: 源代码文件和资源文件(properties)及 hibernate 文 件( *.hbm.xml) 的存放位置 CSS: 样式表存放位置 JSPS:JSP 页面文件的存放位置:主要的页面有 edit.jsp、help.jsp、View.jsp 门户系统 Portlet 开发技术解决方案 Lib:所需要的 JAR 文件 Portlet.xml :Portlet 部署描述符文件 其基本结构如下(具体结构请参考 JSR168-API): Put your portlet description here demo com.olive.demo.demo text/html VIEW EDIT HELP en demo demo demo  Web.xml: 应用部署描述文件  Build.xml: ANT 的发布及部署描述文件 门户系统 Portlet 开发技术解决方案 3.3.1.3. 创建Portlet 在 Portlet 工程中右键点击“新建”选择“其他” 选择 JSR168Portlet: 单击“next”进入工程信息配置 门户系统 Portlet 开发技术解决方案  Source folder:Portlet 工程所在的文件夹  Package:Portlet 的包名(自定义)  Name:Portlet 的名称  Portlet Modes :Portlet 中所用到的主要方法 点击“finish”后可以看到以下的自动生成文件: 打开该文件可以看到主要的类调用: 门户系统 Portlet 开发技术解决方案 该文件为主要 Portlet 开发的流程处理文件 该类继承于直接或间接实现 Portlet 接口的类(有关详细调用请参考 JSR168API 文档规范) 主要用到的类的包有: Javax.portlet.* 关于其他包引用按正常情况即可 主要用到的方法有: doView : Portlet 视图显示的处理方法,处理关于视图显示信息及页面的信息主要和 View.jsp 文件相关,在 VIEW 这个 portlet 模式里,所被期望要提供的功能是 产生 markup 语言来表现此时 portlet 的状态。 举例来说, portlet 的 VIEW 模式可以包含一个或多个画面让使用者可以浏览与互动,或是一些不需要与使用 者互动的静态内容。 Portlet 开发者应该重写 doView 这个被定义在 GenericPortlet 类別里的方法,来执行 VIEW 模式的功能。所有的 Portlet 都 必须支持 VIEW 模式。 事例代码: 门户系统 Portlet 开发技术解决方案 doView 方法: public void doView(RenderRequest req, RenderResponse res) throws IOException, PortletException { PortletContext ctx = getPortletContext(); PortletRequestDispatcher prd = ctx.getRequestDispatcher("/view.jsp"); if (prd == null) { _log.error("/view.jsp is not a valid include"); } else { try { prd.include(req, res); } catch (Exception e) { _log.error(e, e); prd = ctx.getRequestDispatcher("/error.jsp"); if (prd == null) { _log.error("/error.jsp is not a valid include"); } else { prd.include(req, res); } } } 门户系统 Portlet 开发技术解决方案 } 事例JSP: <%@ page language="java" contentType="text/html; charset=GBK" %> <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <%@ page import="com.liferay.portal.kernel.util.Constants" %> <%@ page import="com.liferay.portal.kernel.util.HttpUtil" %> <%@ page import="com.liferay.portal.kernel.util.ParamUtil" %> <%@ page import="com.liferay.samplehibernate.model.FoodItem" %> <%@ page import="com.liferay.samplehibernate.util.FoodItemUtil" %> <%@ page import="java.util.List" %> <%@ page import="javax.portlet.WindowState" %> <% String cmd = ParamUtil.getString(request, Constants.CMD); if (cmd.equals(Constants.ADD) || cmd.equals(Constants.EDIT)) { long foodItemId = 0; String name = ""; int points = 0; if (cmd.equals(Constants.EDIT)) { foodItemId = ParamUtil.getLong(request, "foodItemId"); 门户系统 Portlet 开发技术解决方案 FoodItem foodItem = FoodItemUtil.getFoodItem(foodItemId); name = foodItem.getName(); points = foodItem.getPoints(); } %> <% if (cmd.equals(Constants.EDIT)) { %> <% } 门户系统 Portlet 开发技术解决方案 %>
员工ID <%= foodItemId %>
姓名
薪资

<% if (renderRequest.getWindowState().equals(WindowState.MAXIMIZED)) { %> 门户系统 Portlet 开发技术解决方案 <% } %> <% } else { %> " value="<%= Constants.ADD %>" />';" />

门户系统 Portlet 开发技术解决方案 <% List foodItems = FoodItemUtil.getFoodItems(); for (int i = 0; i < foodItems.size(); i++) { FoodItem foodItem = (FoodItem)foodItems.get(i); %> <% } %>
员工 ID 姓名 薪资 功能
<%= foodItem.getFoodItemId() %> <%= foodItem.getName() %> <%= foodItem.getPoints() %> " value="<%= Constants.EDIT %>" />';" />

<% } %> doEdit : Portlet 编辑功能的处理方法,主要处理 Portlet 在具有编辑功能时的相关 数据(与 Edit.jsp 页面相关), 在 EDIT 这个 portlet 模式里, protlet 需要 提供内容和逻辑来让使用者定制 portlet 的行为。 portlet 的 VIEW 模式可以包含一个或多个画面让使用者可以浏览并输入一些 定制的资料。 门户系统 Portlet 开发技术解决方案 典型的说, EDIT 模式的 portlet 会设定或更新 portlet 的参数设定值。 Portlet 开发者应该重写 doEdit 这个被定义在 GenericPortlet 类別里的方 法,来执行 EDIT 模式的功能。所有的 portlet 并不需要都提供 EDIT 这个模 式。 doHelp: Portlet 帮助功能的处理方法,主要处理 Portlet 在具有帮助功能时的相关 数据(与 Help.jsp 页面相关)在 HELP 这个模式里,portlet 应该提供有关这 个 portlet 的 help 讯息。 这个 help 讯息可以是有关这个 portlet 的简单且条理清楚的视窗说明或是详 细的说明整个来龙去脉。 Portlet 开发者应该重写 doHelp 这个被定义在 GenericPortlet 类別里的 方法,来执行 HELP 模式的功能。所有的 portlet 并不需要都提供 HELP 这个 模式. 事例代码: public void doHelp(RenderRequest portletRequest, RenderResponse portletResponse) throws PortletException { WindowState state = portletRequest.getWindowState(); portletResponse.setContentType("text/html"); PortletContext context = getPortletContext(); PortletRequestDispatcher dispatcher = context.getRequestDispatcher("/WEB-INF/jsps/help.jsp"); try { dispatcher.include(portletRequest, portletResponse); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } 门户系统 Portlet 开发技术解决方案 事例 JSP: <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="UTF-8"%> <%@page import="javax.portlet.*" %> <%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %> Hello World From the Help Mode 门户系统 Portlet 开发技术解决方案 3.3.1.4. 部署Portlet  配置 ANT 部署: 门户系统 Portlet 开发技术解决方案 3.3.2. 在my-eclipse6.6 中开发 3.3.2.1. 建立Portlet工程 3.3.2.1.1. 建立web工程 建立 web 工程 门户系统 Portlet 开发技术解决方案  Project Name:web 工程名称  Location:web 工程的存放位置(点选“USE Default Location”选择默认 工作区)  Source folder:存放源文件的位置  Web root folder: 默认的应用程序启动文件夹  Context root URL:上下文根的 URL 地址  J2EE Specification Level:J2ee 级别 点击“finish”完成工程建立 文档结构如图: 门户系统 Portlet 开发技术解决方案 3.3.2.1.2. 创建Portlet 选中 web 工程后,点击“my-eclipse” 选择“Project Capabilites”中的“Add JSR 168 Portlet Capabilites” 点击“finish” 门户系统 Portlet 开发技术解决方案 完成后出现以下效果: 在 web 工程中右键点击“new”选择“Other” 门户系统 Portlet 开发技术解决方案 选择 my-eclipse 选择其中的“web” 门户系统 Portlet 开发技术解决方案 再选择“Portlet”点击“next” 开始配置 Portlet 信息: 门户系统 Portlet 开发技术解决方案 可以根据需要添加所需的方法覆盖 点击“next” 门户系统 Portlet 开发技术解决方案  Name:Portlet 的类名称  Display name:Portlet 的显示名称  Description: Portlet 的描述信息  Title:Portlet 的标题  Short title: Portlet 的短标题  JSP for view mode :view.jsp 的页面位置  JSP for edit mode :edit.jsp 的页面位置  JSP for help mode :help.jsp 的页面位置 点击“finish”完成设置 门户系统 Portlet 开发技术解决方案 3.3.2.1.3. 工程结构图 相关内容与在 Eclipse 工程结构中介绍的相同,在此部在累述。 例外内容:My-eclipse6.6 中集成了开发符合 JSR168 规范 Portlet 所用的核心 包文件。 主要的类调用文件: 门户系统 Portlet 开发技术解决方案 有关方法说明的内容与前面介绍的内容相同在此不在累述(详细的开发 API 请参 考 JSR168API 文档) 3.3.2.1.4. 部署Portlet 目前采用直接导出为.war 文件的格式进行部署 : 在 Portlet 工程上右键点击“export” 选择“J2EE”中“WAR File” 点击“next”继续,选择 war 包的名称 门户系统 Portlet 开发技术解决方案 点击“finish”完成 war 包创建 将 war 包复制到门户系统的发布路径下即可完成热部署 打开 ANT 视图点击该按钮 门户系统 Portlet 开发技术解决方案 定向到 Portlet 工程中去选择“build.xml” 点击“OK”完成 点击左边的“+”打开列表,选择“deploy”进行部署 (自动生成的是.war 文件 ): 门户系统 Portlet 开发技术解决方案 部署成功的信息: 默认的路径是: 可以在 build.xml 文件中进行修改指向门户系统的部署路径: 修改 value 值即可。 门户系统 Portlet 开发技术解决方案 4. LiferayPortlet开发方案 4.1. 方案说明 此开发方式只适用于 Liferay5.1.1 门户系统的 Portlet 开发,在 Liferay5.1.1 下测试成功,该部分将以实例说明详细开发流程,关于开发的深 度只会起到抛砖引玉的作用,更多的部分请仔细研究 JSR168API 规范。 参考实现资源搜集整理于网上及参照 JSR168API 及 Liferay 的官方文档并由 本人作了些修改和添加,主要为 Liferay 下的 Portlet 开发提供真实依据,由于 本人于水平有限,如有错误,请各位高手指正。 这份文档适合于开发Portlets并且部署在Liferay Portal中的开发者.由于 Liferay 支持Portlet 规范(JSR-168).任何基于此规范的Portlet均可正常运行. 这份文档涵盖了了Liferay的专门的部署标识符,并解释了Liferay Portal提供 给Portlet开发者的工具,附加特性和服务. portal是一种web应用,遵守一系列 的规定,允许portals组织它的生命周期并且与其他portlets集成. 门户系统 Portlet 开发技术解决方案 4.2. 理论概述 4.2.1. Liferay Portal Liferay Portal 包括它自身的portlet容器并且提供了许多功能,例如用户 和组织管理,创建虚拟社区,生成基于portlet布局的页面, portlet的图形选择 和拖&拽来摆放它们,在网站中聚合页面,一些封装好的可以使用的portlets, 和 其他更多的内容. 4.2.2. Liferay 描述 Liferay Portal 有两个具体的部署标识符,扩展了通过portlet.xml文件来 提供的功能. 其中之一提供了用Liferay的具体特性, 第二个允许通过UI来配置, 允许用户选择portlets.下一章节介绍这两个标识符. 4.2.2.1. 扩展的Liferay定义 Liferay-portlet.xml文件可以放置在WEB-INF目录下, 在这个目录下的任 何portlet application可以配置Liferay Portal 的具体特性, 接下来是一个这 个文件的例子: 1 门户系统 Portlet 开发技术解决方案 /html/portlet/mail/icon.png mail false false false false true false false 0 / html/portlet/mail/packed.js ... Portlet-name元素必须与在portlet.xml文件中定义的portlet名字相一致, 下面是一份所有可能选项列表的例子: • Icon Icon元素指定了一个在portlet中显示的图片 • Virtual—path Virual—path 的值指定了一个虚拟路径用于覆盖默认的servlet内容路径. 举个例子,假设你的portlet被部署在servlet路径”/test-portlet”. 通过默认 值, portal将会返回到”/test-portlet”的servlet内容路径. 你可以通过设置 门户系统 Portlet 开发技术解决方案 虚拟路径到”/virtual”来覆盖默认路径, portal 就会返回到”/virtual”的 servlet内容路径.默认值是””, 这意味着没有使用. • Struts-path 这个选项只在用Liferay Struts Portlet 框架时才有用. 假设struts— path的值是”mail”. 这就告诉了portal说所有的路径为mail/*的请求都在这个 portlet’s领域的考虑范围之内.如果用户访问到这个portlet的话,只有路径匹 配mail/*的用户请求才被允许访问. 这对于portlet 请求和标准的servlet请求 同样适用. • Configuration-path Configuration-path 的值是一个Struts 路径,允许用户配置运行时的 portlet. • Indexer-class Indexer-class的值必须是一个实现com.liferay.util.lucene.Indexer的 类,并且被调用来创建,或者更新对于portlet的索引. • Open-search-class Open-search-class 的值必须是一个实现 com.liferay.portal.kernel.search.opensearch的类, 并且以opensearch1.1 的标准来被调用,以获得搜索结果. 门户系统 Portlet 开发技术解决方案 • Scheduler-class Scheduler-class的值必须是一个实现com.liferay.portal.job.scheduler 的类, 并且被调用来确定这个portlet的Schedule Quartz jobs. • Portlet-url-class Portlet-url-class 的值必须是一个继承了 com.liferay.portlet.portleturlimplwrapper 的类.指定这个类来覆盖默认的 portlet URL实现. • Friendly-url-mapper-class Friendly-url-mapper-class 的值必须是一个实现 com.liferay.portal.kernel.portlet.friendlyURLMapper 的类. 如果在 portlet之内的内容要使用一个friendly URL 就要使用这个.它的使用例子在 MessageBoards portlet中. 门户系统 Portlet 开发技术解决方案 • url-encoder-class url-encoder-class 的值必须是一个实现 com.liferay.portal.kernel.servletURLEncoder的类.使用这个来建立一个自 定义的URLEncoder,这个URLEncoder 是供RenderResponse 类来使用,用于实现 encodeURL方法.在你需要添加自定义的逻辑来重写URLs时这个将会非常有用. • Portlet-data-handler-class Portlet-data-handler-class 的值必须是一个实现 com.liferay.portal.kernel.lar.PortletDataHandler的类,并且当归档计划执 行时被调用. • Smtp-message-listener-class Smtp-message-listener-class 的值必须是一个实现了 com.liferay.portal.kernel.smtp.MessageListener 的类, 并且当传输电子信 件时才被调用. 门户系统 Portlet 开发技术解决方案 • Preferences-company-wide 如果这个portlet 的参数在整个公司内都可以访问, 就将 Preferencescompany- wide 的值设定为true,.将这个值设定为true 意味着不能使用 • preference-unique-layout 与preferences-owned-by-group 的值一样.默认值是false.例如,管理员可 以将这些参数放置于一个声明好的portlet中,这个portlet可以以信息的形式来 记录portlet中的参数.这些信息在公司的所有页面中都能访问.这个portlet不 能被实例化,因为实例化的portlet有唯一的自增长的portlet id.被封装的声明 的portlet的默认的行为将实例化的值设定为true,以便普通用户不能创建公司 全局信息.一个更深层的引用将会包括对编辑模式的许可,这种编辑模式是相对 于浏览模式来讲,允许管理员设定信息而用户只能浏览信息. • Preferences-unique-per-layout 如果这个portlet 的参数对每个页面都是唯一的, 就将 Preferencesunique-per-layout的值设定为true..如果将其设定为false,这个 portlet的参数将会在所有页面中被访问.默认值是true. 门户系统 Portlet 开发技术解决方案 • Preferences-owned-by-group 如果这个portlet显示在一个组的页面中并且这个portlet的参数是由这个 组来决定的, 就将preferences-owned-by-group的值设定为true. 如果将其设 定为false,这些参数将会一直被用户拥有.默认值是true. 假设股票portlet 有一个preferences-unique-per-layout, 将其设定为 true并且将preference-owned-by-group设定为false.用户可以对于每个个人页 面来指定不同的股票列表.也可以对每一个社区页面指定不同的股票列表. 假设股票portlet 有一个preferences-unique-per-layout, 将其设定为 false并且将preference-owned-by-group设定为false. 用户可以指定一个股 票列表可以在所有的个人页面中查看.也可以在一个社区页面指定一个股票列 表. 假设股票portlet 有一个preferences-unique-per-layout, 将其设定为 true并且将preference-owned-by-group设定为true. 用户可以对于每个个人 页面来指定不同的股票列表.管理员可以在社区里指定对用户的portlet参数. 管理员可以对每一个社区页指定不同的股票列表,这些页面之后可以被社区内 的所有用户分享. 假设股票portlet 有一个preferences-unique-per-layout 将其设定为 false并且将preference-owned-by-group设定为true. 用户可以指定一个股票 列表可以通过所有的个人页面来分享. 管理员可以在一个社区页中指定对用户 的portlet参数. 管理员可以指定一个股票列表可以通过一个社区的指定页面的 所有用户来分享. 门户系统 Portlet 开发技术解决方案 • Use-default-template 如果portlet 使用默认的模板来装饰和包装内容, 就将 Use-default-template的值设定为true. 将其设定为false允许开发者组织和编 辑portlet的整体输出内容.默认值是true.如果你想要你的portlet与其他 portlet不同,或者如果你想要你的portlet输出内容没有边框,这个值就是最常 用的方式. • Show-portlet-access-denied 如果用户展示的portlet 有一个不允许的访问信息, 就将 Show-portletaccess- denied 的值设定为true, 此信息是由于这些用户不能访问这些portlet.默认值 在poral.properties值指定. • Show-portlet-inactive 将Show-portlet-inactive的值设定为true, 如果用户展示的portlet有一 个不活跃的信息,如果将其设定为false,如果portlet是不活跃的, 用户将不能 显示出这个portlet.默认值在portal.properties中指定. 门户系统 Portlet 开发技术解决方案 • Action-url-redirect 将 Action-url-redirect 的值设定为true, 如果这个portlet 的一个 actionURL可以导致自动的重定向.这可以帮助防止重复提交. 默认值是false. • Restore-current-view 将Restore-current-view的值设定为true, 如果当切换至最大化和正常状 态,portlet恢复到当前视图时.如果将其设定为false, portlet将会重新设定当 前视图,当其切换至最大化和正常状态时.默认值是true. • Maximize-edit 将Maximize-edit的值设定为true, 如果当用户处于编辑状态,portlet正处 于最大化状态时.这只影响默认的portal 图标, 并且此时这个图标不是portlet 开发者能够编辑的.默认值是false. • Maximize-help 将maximize-help的值设定为true, 如果当用户处于编辑状态, portlet正处 于最大化状态时.这只影响默认的portal 图标, 并且此时这个图标不是portlet 开发者能够编辑的.默认值是false. 门户系统 Portlet 开发技术解决方案 • Pop-up-print 将pop-up-print的值设定为true如果portlet正处于弹出状态并且用户正处 于打印模式,这只影响到默认的portal图标, 并且此时这个图标不是portlet开 发者能够编辑的.默认值是true. • Layout-cacheable 将Layout-cacheable标记设定为true,如果在这个portlet所包含的数据不 会改变除非页面布局或者portlet实体改变. • Instanceable 将instanceable的值设定为true, 如果portlet可以在一个页面中显示多次, 如果将其设置为false, portlet只在页面中显示其一次. 默认值是false. • Private-request-attributes 将Private-request-attributes的值设定为true, 如果这个portlet不与其 他portlet共享请求属性. 默认值是true. Private-session-attributes 将Private-session-attributes的值设定为true, 如果这个portlet并不与其他 的portal共享会话属性.默认值是true.性质”session.shared.attributes” 在 portal.properties 指定了哪一个会话属性可以分享即使 private-sessionattributes的值是true. 门户系统 Portlet 开发技术解决方案 • Render-weight 默认值是1.如果将其设定小于1, portlet就会表现出并行性,如果将其设定 为1或者更高, portlet 就会显示出连续性. 拥有一个更高的render-weight 的 portlets有更高的优先级,并且在有低级别的render-weight的portlet之前呈现 出来. 如果ajaxable值设定为false, 最好将render-weight的值设定为1. 这就意 味着如果ajaxable设定为false,ajaxable可以覆盖render-weight的值. • Ajaxable 默认值为true. 如果将其设定为false,portlet将不能通过ajax来显示. Header-css 设定CSS的相关联的路径,这些css是与portal的页面布局的头有关联的样 式. • Header-javascript 设定javascript的相关联的路径,这些javascript是与portal的页面布局的 头有关联的. 门户系统 Portlet 开发技术解决方案 4.2.2.2. Add-default-resource 如果将Add-default-resource的值设定为false, porlet并不依附于页面而 是动态加载, 之后用户会发现他没有权限来查看这个portlet, 如果将 Adddefault-resource的值设定为true,默认的portlet资源和权限被添加到页面 中.用户可以查看这个portlet.大多数的portlets不受这种影响并且可以从这种 灵活性中受益.然而,为了防止安全循环漏洞,默认值是false. • System 将system的值设定为true, 如果portlet是一个系统portlet, 用户不能手 动添加portlet到他们的页面中.默认值是false. • Active 将active的值设定为true, 如果portlet是活跃的并且可供用户使用.如果 将其设定为false, portlet将会不活跃或者不能让用户获取.默认值是true.这 个设定值将会通过管理portlet在运行时改变. • Include 将include 值设定为true 如果portal 会使用这个portlet. 如果将其设定 为false, portlet将会不能被portal使用. 默认值是true. 那些无论活跃或者 门户系统 Portlet 开发技术解决方案 不活跃的portlets,如果它们不包含在portal中,它们将不能被用户使用.由于这 些portlets不会在系统中显示出来, 用户将不会知道这些portlet的存在. 这就 使得Liferay开发者封装一系列的portlets在一个核心包中, 并且允许自定义的 部署方式来开或者关独立的portlets或设定portlets. 这遵循了siebel和 microsoft的捆绑所有在一个核心包中的并且用xml来配置或注册注入来打开或 关掉所有特性或一些特性的模式. 我们并不推荐自定义的部署者通过移去具体 的portlets来修改核心源文件, 因为这防止了今后的轻松升级.最佳的打开或者 关闭portlets的方式就是设定include元素. 这种方式来做事情的优点是变得非 常易于部署Liferay. 所有的特性在一个包中都能使用,缺点是并不能优化所有 的portlets, 所以你浪费了磁盘空间, 甚至可能导致一个微小的静态的内存印 记.然而, 我们认为对于提供一个简单的安装和升级路径来说, 额外的磁盘空间 和内存使用是一个很便宜的代价. • Role-mapper Role-mapper包括两个具体的名称role-name和role-link.role-name值必须 是在portlet.xml中定义的一个具体用户.role-link值必须是Liferay用户存在 于数据库中.role-mapper元素组织了这些值来映射从portlet.xml的用户到 Liferay数据库中的用户. 这是必须的, 因为Liferay用户可能包含spaces无论 portlet.xml中的用户包不包含spaces. 这也增加了额外的灵活性, portlet卖 主并不需要知道任何关于Liferay’s用户的信息. • Role-name 参见role-mapper元素的简介 门户系统 Portlet 开发技术解决方案 • Role-link 参见role-mapper元素的简介 • Custom-user-attribute 自定义的用户属性包括一些名称,这些名称是用一个自定义的扩展了 ccom.liferay.portlet.customuserattributes的类.下载一个简单的关键可部 署的portlet WAR 叫 test.war. 找到 类:com.liferay.portlet.teststruts.teststrutsuserattributes 来观察是否 它关联了自定义的用户属性”user.name.test”与值”test name”.可以修改这 个类来读取从其他数据库中自定义的用户属性,例如LDAP服务器,或一个web服 务. • Name 参见Custom-user-attributer元素的简介 • Custom-class 参见Custom-user-attributer元素的简介 除了定义了上面一些对于每个portlet的具体参数, liferay-portlet.xml文 件也能用于指定用户角色映射和在整个portlet应用中自定义的用户属性.下面 是一个例子: ... user User user.name.random com.liferay.portlet.CustomUserAttributes 下面是对于这些元素的详细描述信息: • Role-mapper Role-mapper包括两个具体的名称role-name和role-link.role-name值必须 是在portlet.xml中定义的一个具体用户.role-link值必须是Liferay用户存在 于数据库中.role-mapper元素组织了这些值来映射从portlet.xml的用户到 Liferay数据库中的用户.这是必须的因为Liferay用户可能包含spaces无论 portlet.xml中的用户包不包含spaces.这也增加了额外的灵活性,portlet卖主 并不需要知道任何关于Liferay’s用户的信息. 门户系统 Portlet 开发技术解决方案 • Custom-user-attribute 自定义的用户属性包括一些名字,这些名字是用一个自定义的类扩展了 ccom.liferay.portlet.customuserattributes. 下载一个简单的关键可部署的portlet WAR 叫 test.war. 找到 类:com.liferay.portlet.teststruts.teststrutsuserattributes 来观察是否 它关联了自定义的用户属性”user.name.test”与值”test name”.可以修改这 个类来读取从其他数据库中自定义的用户属性,例如LDAP服务器,或一个web服 务. 4.2.2.3. JSP Portlet与 Struts Portlet的区别  Struts-config.xml与Tiles-defs.xml结合代替了直接转向JSP.  Struts-config.xml—定义页面流 Tiles-defs.xml—定义页面布局 4.2.2.4. 使用Structs的原因  Struts实现了MVC.虽然有其他框架实现MVC,Struts是最广泛应用和最成熟的 技术. 4.2.2.5. MVC的概念 MVC将显示代码从业务逻辑中分离出来.  Struts在struts-config.xml中提供了集中的页面流管理.这使得它高度可升 级并且使得你模块化代码流程.  通过使用struts,你可以使用一些最佳实践,已经集成在框架中. 门户系统 Portlet 开发技术解决方案 4.2.2.6. Tiles概念 一个页面布局是一个使用包含典型声明的设计.如果有100个JSPs并且头和 尾都需要改变,所有的100JSPs都需要改变.用tiles,一个简单的模板可以被用于 定义页面布局.只要模板改变,所有页面将会依次改变. 4.2.2.7. 高度总览  一个URL或Uri通过控制器传送.控制器决定哪个页面应该被显示出来. 门户系统 Portlet 开发技术解决方案 例子: 4.2.2.8. Liferay如何决定首显页面  我们开始页面指向portlet-ext.xml的view-action  控制器MainServlet.java 详细视图: 门户系统 Portlet 开发技术解决方案 配置文件放在这个目录下: …\docroot\WEB-INF目录下 JSPs将会放在这个目录下: …\docroot\html\portlet\目录下 门户系统 Portlet 开发技术解决方案 4.2.2.9. 分类组织portlet 这个接口提供给用户, 用户可以选择portlet添加到页面中,页面以分类形 式组织了portlets,以便其易于找到. 接下来是一个这样的文件看上去的样子: ... 目录的名称必须是一个封装在portlet资源中定义的键.id属性的值必须是 在portlet.xml文件中定义的portlet-name.目录可以重叠,默认的portal目录可 以用来添加portlet. 门户系统 Portlet 开发技术解决方案 4.2.2.10. Liferay 服务 Portlet应用程序可以调用Liferay Portal提供的服务,这是通过使用 portalclient.jar客户端库来实现的.也可以使用Liferay 服务生成器来开发你 自己的portlets, 这是同样基于Liferay Portal的服务面向体系结构的.余下的 章节介绍了使用中的最重要的服务. 可以在公共的Liferay Wiki的文档中寻找 更多信息. 4.2.2.10.1. 安全和许可服务 许可服务是Liferay Portal 提供给开发者的,可以在他们自己的Portlet中 添加安全相关的功能组件. 目前, 这种功能只提供给有Liferay源码的portlets 开发者, 或者通过已有的环境. 4.2.2.10.2. 用户服务 用户服务允许对portal用户和它的社区(或组)进行管理. 它可以通过静态 的方法UserServiceUtil来访问. 下面就是一个这个最重要的方法的描述. public static com.liferay.portal.model.User addUser( java.lang.String companyId, boolean autoUserId, java.lang.String userId, boolean autoPassword, java.lang.String password1, java.lang.String password2, boolean passwordReset, java.lang.String emailAddress, java.util.Locale locale, java.lang.String firstName, java.lang.String middleName, java.lang.String lastName, 门户系统 Portlet 开发技术解决方案 java.lang.String nickName, java.lang.String prefixId, java.lang.String suffixId, boolean male, int birthdayMonth, int birthdayDay, int birthdayYear, java.lang.String jobTitle, java.lang.String organizationId, java.lang.String locationId, boolean sendEmail) throws com.liferay.portal.PortalException, com.liferay.portal.SystemException, java.rmi.RemoteException; 根据提供信息添加一个用户属性. public static com.liferay.portal.model.User updateUser( java.lang.String userId, java.lang.String password, java.lang.String emailAddress, java.lang.String languageId, java.lang.String timeZoneId, java.lang.String greeting, java.lang.String resolution, java.lang.String comments, java.lang.String firstName, java.lang.String middleName, java.lang.String lastName, java.lang.String nickName, java.lang.String prefixId, java.lang.String suffixId, boolean male, int birthdayMonth, int birthdayDay, int birthdayYear, java.lang.String smsSn, java.lang.String aimSn, java.lang.String icqSn, java.lang.String jabberSn, java.lang.String msnSn, java.lang.String skypeSn, java.lang.String ymSn, java.lang.String jobTitle, java.lang.String organizationId,java.lang.String locationId) 门户系统 Portlet 开发技术解决方案 throws com.liferay.portal.PortalException,com.liferay.portal.SystemException ,java.rmi.RemoteException; 根据提供信息修改用户属性 public static void addGroupUsers(java.lang.String groupId, java.lang.String[] userIds) throws com.liferay.portal.PortalException, com.liferay.portal.SystemException, java.rmi.RemoteException; 根据标识的groupId添加一组用户到社区(组)中. 4.2.2.11. 总结 在阅读完这份文档之后你应该对你所需要的开发和在Liferay 中部署一个 portlet有一种清晰的认识.这份文档提供给portlet开发者的最重要的资源和服 务. 4.2.2.12. Liferay权限 4.2.2.12.1. 简介 合适的细化权限是Liferay portal 中一个主要的新特性.现在, 开发者可以 实现通过安全检查来进入到他们自定义的portlets中, 这就给了用户和管理员 更多的关于他们portlets和内容的控制. 这份文档将会对于在他们自定义的 portlets中实现这种新功能提供一份参考. 在继续浏览这份文档之前, 开发者 应该首先阅读Liferay用户指南的安全和许可检查章节. 门户系统 Portlet 开发技术解决方案 4.2.2.12.2. 概要 在自定义的portlets中添加一个合适的,细化的权限要考虑如下四个步骤 (同样适用于DRAC): 1. 定义所有的资源和他们的权限. 2. 对于在步骤1中指定的所有资源,在权限系统中注册它们, 这就是熟知的”添 加资源”. 3. 将这些资源与必须的许可联系起来. 4. 在返回资源前检查权限. 4.2.2.12.3. 实现权限 这部分中, 在添加Liferay’s安全特性到自定义的Portlets中的四个部分的 每一个部分都要被详细说明. 下面是两个必须要记住的重要定义. 4.2.2.12.4. 资源 这是对portal中的任何对象的通称. 资源的例子包括portlets (例如: 消 息版面,日历等等), Java类(例如: 消息版面主题, 日历,事件等等)和文件 (例 如: 文档, 图片等等). 门户系统 Portlet 开发技术解决方案 4.2.2.12.5. 权限 在资源中执行的行为. 例如: 视图“浏览日历portlet”就在Liferay中作 为 一种权限被定义. 要记住, portlet资源的权限的实现是与其他资源的实现有所不同, 例如 Java 类和文件. 在下文的每一个子章节, 首先解释实现portlet资源权限, 之后是模 型(和文件)资源. 定义资源和行为 对于自定义的portlet,Liferay Portal 需要知道是否有资源需要权限和是 否有自定义的权限. 默认配置被封装到XML 文件中, 此文件可以在目录 portal/portal-ejb/classes/resource-actions中找到, 你也可以使用这个作 为一种参考来为你的portlet创建一个类似的文件. 如果你的porltet仅仅需要 浏览并且配置权限, porltet并不需要使用任何有权限的模型, 那你就不需要创 建这个文件了. 原因是在Liferay中, 所有的portlets自动继承这些权限.然而, 如果你的portlet并不需要自定义权限并且/或者使用的视图有自定义的权限.你 就要创建这个xml 文件来定义资源和行为. 我们来看一下blogs.xml 文件 portal/portal-ejb/classes/resource-actions 并且观察是否blogsportlet定 义了这些资源和行为: 33 门户系统 Portlet 开发技术解决方案 ADD_ENTRY CONFIGURATION VIEW VIEW VIEW ADD_ENTRY com.liferay.portlet.blogs.model.BlogsCategory 33 DELETE PERMISSIONS UPDATE VIEW VIEW 门户系统 Portlet 开发技术解决方案 VIEW UPDATE com.liferay.portlet.blogs.model.BlogsEntry 33 ADD_COMMENT DELETE PERMISSIONS UPDATE VIEW VIEW VIEW UPDATE 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.1. Portlet资源 在 xml 文件中, 首先定义的是portlet 自身. 在根元素 的右下方, 有一个叫的子元素. 在这个元素中,我们定义了portlet名称, 在我们的例子中是33. 接下来, 我们 用标签来列举了这个portlet所支持的所有行为. 要记住这是 portlet级别的.为了理解我们在这里列举的元素, 开发者自己应该了解哪一个 行为属于portlet自身或者在portlet中实现的哪一个行为需要安全检查.在我们 的例子中, 用户需要安全检查来添加一个实体(ADD_ENTRY), 配置博客portlet 选项(CONFIGURATION), 浏览博客自身(VIEW). 每一个支持的权限都写在他自己 的tag中.在我们定义了所有需要检查的行为后, 我们继续定义一 些默认的权限选项.community-defaults标签定义了默认情况下, 在这个社区 (组)中的portlet有什么样的行为. 以另外一种方式来说, 一个用户在最低程度 上访问这个社区时能做什么呢?对于博客portlet来说, 一个可以访问包含博客 portlet社区的用户应该能够浏览博客.同样地, 标签guest-defaults 定义了当 一个客人访问到包含这个portlet的版面时,哪些行为默认是允许的. 所以如果 一个访客可以访问到包含博客portlet的社区页时,访客应该在最低程度上, 根 据blog.xml文件的定义, 就能够浏览这个portlet(对portlet中的内容并不是必 须的).否则,访客就会在portlet中看到一个错误信息. 依照你自定义的portlet, 你可以在这里添加更多有意义的行为. guest-unsupported标签包括了一个访客 不应该做的行为. 例如:客人访问这个博客时就不应该能够添加一个博客实体因 为博客是属于一个用户或一组用户的. 所以即使一个用户想要同意一个访客在 他的博客中添加博客实体这也是不可能的, 因为blog.xml并不允许客人做这种 行为. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.2. 模型资源 在作为资源的portlet 被定义之后, 我们接下来定义那些需要接受检查的 portlet模型. 模型资源被标签环绕着. 在这个标签中,我们 首先定义模型的名字.这必须完全遵循Java关于模型类名定义的规范. 接下来我 们用标签来定义这个模型隶属于的portlet名称, 虽然未必一个 模型隶属于多个portlets, 但是也可以用多个标签来定义.类似 于portlet资源元素, 模型资源元素也允许你定义需要权限来实现的行为支持列 表. 你必须列出所有需要安全检查的来实现的行为.由于你可以浏览博客, 用户 必须有权限来添加对博客的评论, 删除博客, 对博客的安全选项做出改变, 修 改博客, 或者简单的浏览博客.标签,标 签,标签在意义上都是类似的,我们将在portlet资源中来 解释. 4.2.2.12.5.3. Default.xml 在定义了对你的自定义的portlet的权限设计之后,你必须告诉Liferay文件 放置的位置. 对于Liferay 核心来说,xml 文件必须放置在 portal/portalejb/classes/resource-actions 中, 并且对于这个文件的链接 要放在default.xml 中 . 创建一个叫default-ext.xml 的文件此文件是与 default.xml类似的.在default-ext.xml添加你所有的自定义的资源-行为xml文 件. 之后在portal.properties 复制属性resource.actions.configs 粘贴到 portalext.properties.最后,在属性值后添加一个逗号, 添加路径到你的 default-ext.xml文件.(例如resource.actions.configs= resource-actions/default.xml,resource-actions/default-ext.xml),下面是 一个default.xml文件的例子. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.4. 添加资源 在定义资源和行为后,下一个工作就是写代码来添加资源到权限系统.许多 添加资源的逻辑都被封装到ResourceLocalServiceImpl类中.所以添加资源是一 种非常方便的叫做ResourceLocalServiceUtil的添加资源方法. public void addResources( String companyId, String groupId, String userId, String name, String primKey, boolean portletActions, boolean addCommunityPermissions, boolean addGuestPermissions); 对于所有的需要访问权限的Java对象来说,你需要确定他们都是被作为资源 来添加并且每次一个新的资源将会被创建. 例如:每次用户添加新的实体到他的 博客中, 就会调用addResources()方法来添加新的实体到资源文件中. 下面是 一个来自于BlogsEntryLocalServiceImpl类的调用的例子. ResourceLocalServiceUtil.addResources( entry.getCompanyId(), entry.getGroupId(), entry.getUserId(), 门户系统 Portlet 开发技术解决方案 BlogsEntry.class.getName(), entry.getPrimaryKey().toString(), false, addCommunityPermissions, addGuestPermissions); 参数companyId, groupId, 和userId 必须自己说明.对于资源对象来说,参 数name 是一个完全限定Java 类. 参数primKey 是资源对象的主键.对于参数 portletActions来说, 如果你要添加一个portlet action的话就要将它设定为 true.在我们的例子中,我们将其设定为false因为我们添加的是一个模型资源, 这个模型资源应该与权限有联系,这个权限是定义blogs.xml文件中的模型行为 有关系. 参数addCommunityPermissions和 addGuestPermissions由用户输入. 如果将其设定为true, 对于这个portlet 来说, ResourceLocalService将会分 别添加默认的权限到社区组中和客人组中. 4.2.2.12.5.5. UI接口 如果在你的自定义的portlet中,你想要提供给你的用户一种能选择添加默 认的社区权限和对资源的访客权限, Liferay有一套自定义的JSP标签你可以迅 速的添加这种功能. 仅仅添加标签到你的 合适的JSP中,这个检验栏将会显示在你的JSP中.当然,要保证这个标签在恰当的
标签中. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.6. 删除资源 为了防止有很多失效的资源占有Resource数据库表的空间,当资源不再被应 用时,你必须记得将它们从Resource 数据库表中移除.只需简单的调用 ResourceLocalServiceUtil的deleteResource(…)方法.下面是一个博客实体被 移除的例子: ResourceLocalServiceUtil.deleteResource( entry.getCompanyId(), BlogsEntry.class.getName(), Resource.TYPE_CLASS, Resource.SCOPE_INDIVIDUAL, entry.getPrimaryKey().toString()); 添加权限 4.2.2.12.5.7. Portlet权限 在portlet级别, 为了让权限系统在你的自定义的portlet中运行, 你并不 需要写任何代码. 你的自定义的portlet将会自动拥有所有的权限特性.如果你 已经在你的portlet-resource中定义了任何自定义的权限(支持的行为), 那些 权限将会自动添加到权限列表中并且用户可以选择他们.当然,对于你有任何值 的自定义的权限来说, 在你的portlet中, 你将会需要显示或者隐藏这种功能. 你可以通过在执行这种功能之前首先检查权限. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.8. 模型权限 为了允许在模型资源中建立权限, 你需要将权限接口提供给用户. 这可以 在你的JSP 中通过添加两个Liferay UI 标签来实现. 第一个是 标签,这个标签返回一个URL,这个URL的用 途是将用户定向到权限配置界面。 第二个标签是, 显示一个 权限图标给用户.下面这个例子是在文件view_entry_content.jsp中找到的. 你在第一个tag 中提供的属性是modelResource, modelResourceDescription, resourcePrimKey, 和var. modelResource属性是完全限定的Java对象类名.之 后这个名称在Language.properties转义到一个更可读的名称 .model.resource.com.liferay.portlet.blogs.model.BlogsEntry=Entry 门户系统 Portlet 开发技术解决方案 对于modelResourceDescription属性来说,你可以传进去任何可以描述这个模型 实例的值.在例子中,博客的标题被传进去了.就像在图例3.3.2.1中蓝色下划线 表示的一样. resourcePrimKey属性只是你模型实例的主键.var属性就是变量名, 将会分配到这个URL字符串.这个变量将会传给标签以便权限 图标将会有合适的URL链接.也有一个可选择的属性redirect,在图例3.3.2.1中 的右上角有一个链接, 如果你想覆盖这个链接的行为就可以选择这个属性.这就 是你必须做的使得用户能够对模型资源配置权限选项!! 检查权限 为了在你的自定义的portlet中实现权限, 主要的步骤是检查权限.这可能 在一些地方来完成.例如, 在删除一个资源前,如果用户没有权限来做这个事情, 你的业务逻辑层就应该检查权限,或者在你的用户接口中隐藏一个按钮来添加一 个模型(例如:一个日历事件). 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.9. 检查portlet资源权限 类似于其他步骤, 对这个porltet资源的默认的权限将会自动检查. 无论用 户被允许浏览或者配置portlet自身时, 你并不需要对你的portlet来做任何事 情来加以区分. 然而, 你需要实现任何自定义的权限, 这些权限是在你的 resource-actionsXML文件中定义的.在例子中的博客portlet中, 一个自定义的 支持行为就是ADD_ENTRY. 在源代码中有两个位置来检查这个权限. 第一个位置是在文件view_entries.jsp中的, 这个add_entry按钮是不是一 直存在的,无论这个用户是否有权限来添加实体(and also whether the user isin tab one) <% boolean showAddEntryButton = tabs1.equals("entries") && PortletPermission.contains(permissionChecker, plid, PortletKeys.BLOGS, ActionKeys.ADD_ENTRY); %> 第二个位置来检查这个add entry 权限是在文件BlogsEntryServiceImpl 中.(注意到这个文件和BlogsEntryLocalServiceImpl的区别)在addEntry(…)方 法中, 无论到来的请求是否有权限来add entry, 检查都会执行. PortletPermission.check( getPermissionChecker(), plid, PortletKeys.BLOGS, ActionKeys.ADD_ENTRY); 如果检查失败, 将会抛出一个PrincipalException并且add entry请求中断. 你可能也想知道PortletPermission 和PermissionChecker做了什么. 我们来看 看这两个类吧. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.10. PERMISSIONCHECKER PermissionChecker类有一个方法叫做hasPermission(…), 这个方法检查 用户是否有访问权限来生成一个资源请求. 如果用户没登录(访客), 方法将返 回访客权限,否则,返回用户权限. 这个类在两个位置可以获得. 首先在你的业 务逻辑层, 你可以在你的ServiceImpl类中通过调用getPermissionChecker()方 法来获得一个PermissionChecker 的实例.这个方法是可获得的因为所有的 ServiceImpl ( 不是LocalServiceImpl) 继承了PrincipalBean 类 , 实现了 getPermissionChecker() 方法. 另外的一个地方你能获得一个 PermissionChecker类的实例的位置是在你的JSP文件中.如果你的JSP文件获得 了portlet标签或者包含的另外一个JSP 文件有这 个标签, 你将通过变量permissionChecker 来获得类PermissionChecker的一个 实例.现在你知道PermissionChecker如何运作的并且如何获得它的一个实例, 我们来看看Liferay在使用它的便利性吧. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.11. PORTLETPERMISSION PortletPermission是一个辅助类使得你对portlet资源的权限检查变得非 常容易(相对于模型资源,将在3.4.2章节讲到)它有两个静态方法一个叫做 check()另一个叫做contains().他们同样都是基本的.他们的区别是: 1.只有一个check()方法和一个contains()方法可以取得portlet页面ID变 量 (plid). 2. 如果用户没有权限,check() 方法将会抛出一个新的 PrincipalException,contains()方法将会返回一个布尔型的指示是否用户有权 限.contains()方法意味着可以在JSP文件中使用因为它返回一个布尔型而不是 抛出一个异常. check()方法可以在你的业务逻辑层来调用(ServiceImpl). 让 我们重新观察下面的blog 例子. ( 在BlogsEntryServiceImpl 中可以找到 addEntry(…)方法). public BlogsEntry addEntry( String plid, String categoryId, String[] tags, String title, String content, int displayDateMonth, int displayDateDay, int displayDateYear, int displayDateHour, int displayDateMinute, boolean addCommunityPermissions, boolean addGuestPermissions) throws PortalException, SystemException { PortletPermission.check( getPermissionChecker(), plid, PortletKeys.BLOGS, ActionKeys.ADD_ENTRY); return BlogsEntryLocalServiceUtil.addEntry( 门户系统 Portlet 开发技术解决方案 getUserId(), plid, categoryId, tags, title, content, displayDateMonth, displayDateDay, displayDateYear, displayDateHour, displayDateMinute, addCommunityPermissions, addGuestPermissions); } 在addEntry(…)方法调用BlogsEntryLocalServiceUtil.addEntry(…)来添 加一个博客实体之前, 它调用了PortletPermission.check(…)来检查用户权限. 如果检查失败, 就会抛出PrincipalException没有实体将会被添加.注意到在这 个方法中传进来的参数. 再一次, getPermissionChecker() 方法在所有 ServiceImpl类中都能获得. plid变量被传入到方法中通过它的调用者(很有可 能是一个PortletAction类). PortletKeys.BLOGS仅仅是一个静态的字符串说明 这个权限检查是针对博客portlet的. ActionKeys.ADD_ENTRY也是一个静态字符 串来说明这个权限检查所需要的行为.同样地,鼓励你用你自定义的portlet名称 和自定义的action_keys.是否传入portlet页面ID(plid)依赖于你自定义的 portlet是否支持多种实例.我们拿信息版面portlet来做例子.一个社区可能需 要三个不同的页面布局, 每一个需要独立的一个信息版面portlet.只有通过使 用portlet页面ID才能使权限系统能够区分这三个信息版面portlet的实例. 虽 然在大多数上, 大多数portlets并不需要页面布局ID来关联到权限系统. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.12. 服务与本地服务 因为ServiceImpl类继承PrincipalBean类, 它需要访问当前用户的信息来 做这个服务请求.因此, 在业务逻辑中, ServiceImpl类是一个非常理想的位置 来放置你的权限检查.Liferay的便利性是说在LocalServiceImpl中实现这个实 际的业务逻辑, 在成功权限检查之后, ServiceImpl通过LocalServiceUtil类来 调用这些方法. 你的PortletAction 类应该调用ServiceUtil ( 包装在 ServiceImpl), 保证在完成请求之前首先检查权限. 4.2.2.12.5.13. 检查模型资源权限 检查模型资源权限非常类似于检查检查portlet 资源权限.主要的区别是不 是调用上文提及的PortletPermission类中的方法,你只需要创建你自己的辅助 类来助你进行检查.下一章节将详细解释如何执行. 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.14. CUSTOM PERMISSION CLASS 在你自定义的模型中拥有一个辅助类来帮助做检查权限是明智的. 这个自 定义的权限类类似于PortletPermission类, 但是它适合在你的自定义的模型中 运行.如果你喜欢的话你可以实现这个类, 我们推荐你模仿PortletPermission 类,它包含四个静态方法.我们来看一下BlogsEntryPermission类. public class BlogsEntryPermission { public static void check( PermissionChecker permissionChecker, String entryId, String actionId) throws PortalException, SystemException { if (!contains(permissionChecker, entryId, actionId)) { throw new PrincipalException(); } } public static void check( PermissionChecker permissionChecker, BlogsEntry entry, String actionId) throws PortalException, SystemException { if (!contains(permissionChecker, entry, actionId)) { throw new PrincipalException(); } } public static boolean contains( PermissionChecker permissionChecker, String 门户系统 Portlet 开发技术解决方案 entryId, String actionId) throws PortalException, SystemException { BlogsEntry entry = BlogsEntryLocalServiceUtil.getEntry(entryId); return contains(permissionChecker, entry, actionId); } public static boolean contains( PermissionChecker permissionChecker, BlogsEntry entry, String actionId) throws PortalException, SystemException { return permissionChecker.hasPermission( entry.getGroupId(), BlogsEntry.class.getName(), entry.getPrimaryKey().toString(), actionId); } } 另外, 这两个check(…)方法意味着在可以在你的业务逻辑层中被调用,两 个 contains() 方法可以在JSP 文件中使用. 就像你看到的, 非常类似于 PortletPermission类. 它们两个的显著区别是: 1. 不是用portletId作为参数之一,在这个自定义类中的方法可以使用entryId 或者BlogEntry对象. 2. 这两个方法都不需要接收portlet页面ID(plid)作为参数. (如果需要的话, 你自定义的portlet可以选择使用portlet页面ID) 让我们来看这个类是如何在博客portlet代码中使用的. public BlogsEntry getEntry(String entryId) throws PortalException, SystemException { BlogsEntryPermission.check( getPermissionChecker(), entryId, ActionKeys.VIEW); 门户系统 Portlet 开发技术解决方案 return BlogsEntryLocalServiceUtil.getEntry(entryId); } 在BlogsEntryServiceImpl类中一个方法叫做getEntry(). 在这个方法返回 博客实体对象之前, 它调用了自定义的权限辅助类来检查权限. 如果这个调用 并不抛出异常, 实体将会重新获得并返回到它的调用者. 在view_entry_content.jsp文件中, BlogsEntryPermission.contains(…) 方法被调用来检查是否显示编辑按钮.这就是所有做的事情!!!!! 门户系统 Portlet 开发技术解决方案 4.2.2.12.5.15. 总结 我们来回顾一下我们上面的内容. 在你的自定义portlet中实现权限需要四 个步骤.首先是定义任何自定义的资源和行为. 接下来的步骤是实现代码来注册 (或添加)任何新创建的资源例如BlogsEntry对象. 第三步是对用户提供一个接 口来配置权限. 最后, 在返回资源或者自定义的特性之前, 实现代码用来检查 权限. 两个主要的资源是portlets和Java对象. 对于portlet资源来实现权限系 统来说, 并不需要做很多工作, 因为LiferayPortal已经为你做了很多工作.你 主要的关注点是对任何你建立的Java对象上. 你现在已经对你自定义的Liferay Portlets 实现了安全性!对于其他的用户指南, 请访问Liferaydocumentation page. 门户系统 Portlet 开发技术解决方案 4.2.2.13. 角色 如果你想知道Liferay的用户角色, 社区角色, 组织/本地角色是如何在你 的portlet中实现的, 这份简短的章节将会提出它. 简洁的回答是, 对于用户角 色,社区角色, 组织/本地角色并不需要具体的开发, Liferay的权限系统已经为 你的自定义portlets 做好了. 当 PermissionChecker 类调用 hasUserPermission(…)方法, Liferay 检查所有的当前用户拥有的所有角色, 无论他们是用户角色, 社区角色, 或者组织/本地角色.在Liferay中使用你自己 的安全系统这是一份简短的大纲指出如何在Liferay中使用你自己的安全系 统.· 创建你自己的 PermissionChecker类继承 Liferay’sPermissionChecker c类. · 在 portal.properties中, 在permissions.checker 属性下面,注册这个新类 (或者现有环境中 portal-ext.properties ). · 运用你自己的对你权限系统的调用方式来覆盖hasUserPermission(…)方法和 hasGuestPermission(…) 方法 · 你可以调用setValues(…)方法来从你可能需要权限检查的请求对象中注入值 (例如userId, projected,等等). · 你可以调用resetValues(…)方法来重设旧的参数.. · 覆盖isAdmin(…)方法. 门户系统 Portlet 开发技术解决方案 4.2.2.14. 数据库表结构视图 重新审视Liferay是如何在数据库中存储所有的权限信息, 可以帮助你获得 一个关于整体权限系统的更好地理解. Resource_表包括所有的注册资源. 资源所做出的, 每一个可能的安全行为将会在permission_表中产生一行结 果.例如, BlogEntry资源将会在permission_表中有一行是针对于浏览行为, 另 外 一行是关于修改行为. 用户是否有权限来取得资源依赖于用户所拥有的角色,或者是用户所在的社 区(组)和组织(绿色表). 如果那些角色或组包含所需的permissionId, 存在于 permissions表(蓝颜色)中.用户就会有访问资源的权限. 门户系统 Portlet 开发技术解决方案 4.3. 安装配置步骤 4.3.1. 开发SDK安装 从官网下载 liferay-plugins-sdk-5.1.1.zip 后将该文件包解压到任意路径下 即可,以下以 E:\liferay-plugins-sdk-5.1.1 为例: 将 Zip 压缩包解压到 E:\liferay-plugins-sdk-5.1.1,打开后可以发现如下文 件: 转到 E:\liferay-plugins-sdk-5.1.1\portlets 目录,该目录包括以下文件: create.bat:开发自动生成 Portlet 工程的主要批处理文件 build.xml:自动热部署的 ant 部署文件 create.sh: Linux 下的运行 bat 文件 Portlet.zip :Portlet 开发框架的基础模板 门户系统 Portlet 开发技术解决方案 4.3.2. 环境变量配置  配置 ANT_HOME 例如:c:\ant  配置 JIKES_Home 例如:c:\jikes  将 c:\jikes\bin 下的 jikes.exe 复制到 c:\jdk1.6\bin 下  测试环境 ANT 是否配置成功: 在“cmd”控制台下:输入“ant -version”进行验证: 出现“Apache Ant version 1.7.1 compiled on June 27 2008”字样表示 ant 配置成功。 4.3.3. 配置文件修改 修改 E:\liferay-plugins-sdk-5.1.1 下的 build.properties: app.server.dir=”你实际 Liferay 所存放的位置” 以 d:\Liferay 为例, 注意此文件中应输入:“d:/liferay”,修改完成后保存文件 门户系统 Portlet 开发技术解决方案 4.4. 开发步骤 4.4.1. plugins开发方式 次开发方式只适用于 Liferay 下开发 Portlet,详细的开发请参考 Liferay 开发 参考实现。 4.4.1.1. 创建Portlet工程 在“cmd”控制台下转到 E:\liferay-plugins-sdk-5.1.1\portlets 目录下 进行 Portlet 工程的创建: 命令格式 create.bat 项目名 “Portlet 名称” 在“ cmd”控制台下转到 E:\liferay-plugins-sdk-5.1.1\portlets 目录下输入: create.bat Hello World “Hello World” 后回车等待建立: 门户系统 Portlet 开发技术解决方案 若出现以上信息表示 Portlet 创建成功。 回到 E:\liferay-plugins-sdk-5.1.1\portlets 中可以发现: 文件夹“HelloWorld-portlet”即为所创建的 Portlet 工程打开后会发现 门户系统 Portlet 开发技术解决方案 可以将以上的文件导入到 my-eclipse6.6 的 IDE 中进行开发: 4.4.1.2. Portlet工程导入 在此采用 My-eclipse6.6 进行开发此工程:  在 my-eclipse6.6 中新建“WEB”工程  将工程路径定向到 E:\liferay-plugins-sdk-5.1.1\portlets\Hello-portlet\下:  Web root folder 选择:docroot(不要更改)  点击“finish”完成工程创建 导入标签库  为了使用 Portlet 自定义标签将门户系统中 D:\liferay\webapps\ROOT\WEB-INF 下的 tld 文件夹复制到 E:\liferay-plugins-sdk-5.1.1\portlets\Hello-portlet\docroot\WEB-INF 门户系统 Portlet 开发技术解决方案 下 门户系统 Portlet 开发技术解决方案 4.4.1.3. 工程结构图 导入后的文档结构: 开发相关的部分:  SRC:编写 Portlet 的主要类文件的存放位置  Portlet.xml 文件:主要的 Portlet 部署描述符文件  view.jsp:Portlet 的 view 模式下的显示页面  edit.jsp:Portlet 的 EDIT 模式下的显示页面  help.jsp:Portlet 的 HELP 模式下的显示页面 门户系统 Portlet 开发技术解决方案 4.4.1.4. Portlet部署 Portlets可以通过Liferay’s插件管理系统或者通过应用服务器的规范机制 来手动部署. 插件管理系统 插件管理系统是Liferay的新特性, 允许portal管理员管理和安装portal 中的插件. 一个插件是一种软件组件, 扩展了Liferay.Liferay5 支持上文涉及 到的三种类型的插件(Portlets,Themes和Layout Templates)并且允许:浏览被 移除的那些插件并且显示信息.(关于插件管理系统详情参考《门户系统技术说 明 》)  通过webUI安装新的插件:  从插件资源中选择一个  上传它  指定一个URL, portal可以从此下载.  当一个新插件可获得时通知管理员自动检查安装的插件的新版本.  升级安装的插件. 插件管理系统只运行在这些应用服务器上,在这些服务器上可以热部署.列 表是: JBoss+Jetty, JBoss+Tomcat, Jetty, Resin, Tomcat or WebSphere. 门户系统 Portlet 开发技术解决方案 4.4.1.4.1.1. 插件安装热部署 插件安装可以通过两种方式访问:  通过在portal页面中添加一个portlet.  通过从管理或升级管理portlets来点击”add more portlets”. 插件安装允许管理员以上文列出的三种方式来安装插件,但是需要现有的路 径配置作为部署目录.这个路径的默认值是设定在配置文件portal.properties 中的${user.home}/liferay/deploy.但是我们可以修改这个路径,修改的方式有 两种,一种是通过文件portal-ext.properties来修改,另外一种是在屏幕下方显 示的插件安装portlet的配置标签来修改. 4.4.1.4.1.2. 复制到自动部署目录 在文件系统中, 通过将插件WAR文件手动复制到自动部署配置目录下,我们 也可以执行一个热部署, 当我们可以访问Liferay安装文件时, 还有可以自动处 理进程, 将多个服务器部署到集群中时, 这种方式就是非常便利的. 门户系统 Portlet 开发技术解决方案 4.4.1.4.1.2.1. Ant命令部署(推荐) 也可以通过应用服务器提供的机制来部署web应用程序.但是在部署之前,对 WAR文件做一些检查是必须的,这样Liferay Portal 才会之一到新的portlet应 用已经被部署了. com.liferay. portal.tools.PortletDeployer就是处理这件 事情的. 推荐你使用ant脚本工具来做这件事情 在CMD命令控制台中指向SDK的 Portlet目录例如: E:\liferay-plugins-sdk-5.1.1\portlets\Hello-portlet 输 入 :“ ant deploy”后进行部署: 部署生成的 war 文件将自动发布到 Liferay 的热部署文件夹(倒数第二行显 示)下完成热部署操作。 部署成功后添加到 Liferay 下的界面: 门户系统 Portlet 开发技术解决方案 4.5. 开发进阶 4.5.1. 创建Struts Portlet 通过 create.bat 命令生成的 portlet,是一个最简单的 portlet,如果只是 做普通的显示,是可以满足需要的。但如果是一个复杂的则是不能满足的,需要 做些修改才行,下面是一个 StructsPortlet 的实例: 门户系统 Portlet 开发技术解决方案 4.5.1.1. 程序逻辑: 门户系统 Portlet 开发技术解决方案  关键概念: 门户系统 Portlet 开发技术解决方案 门户系统 Portlet 开发技术解决方案 主要修改的部分包括: 4.5.1.2. 修改portlet.xml文件 根据自己需要修改该文件 1 表示Portlet在Liferay中名称,此名称是唯一的 Sample Struts 表示Portlet 在 Liferay 中显示的名称,此名称不是唯一的 org.apache.portals.bridges.struts.StrutsPortlet

Portlet所调用的类位置 ServletContextProvider com.liferay.util.bridges.struts.LiferayServletContextProvi derWrapper EditPage Structs的action名称 /portlet_action/sample_struts_portlet/edit Structs的action跳转路径 HelpPage /portlet_action/sample_struts_portlet/help ViewPage /portlet_action/sample_struts_portlet/view 门户系统 Portlet 开发技术解决方案 0 text/html edit help Sample Struts Sample Struts Sample Struts administrator guest power-user user
门户系统 Portlet 开发技术解决方案 4.5.1.3. 增加struts-config.xml文件  增加 struts-config.xml 文件到/WEB-INF/目录下: 该文件的一个样例: 门户系统 Portlet 开发技术解决方案 Struts-config.xml定义页面流 添加另外的路径到页面流 观察转向节点 门户系统 Portlet 开发技术解决方案 4.5.1.4. 增加titles-def.xml文件  增加 titles-def.xml 文件到/WEB-INF/目录下: 定义页面布局. 添加错误和成功路径 门户系统 Portlet 开发技术解决方案 4.5.1.5. 修改liferay-portlet.xml文件 根据自己需要修改该文件 1 定义Portlet 在 Liferay 中的名称,此名称是唯一的 com.liferay.portal.apache.bridges.struts.Lifer ayStrutsPortletURLImpl Liferay API的实现类直接引用 true true 定义权限划分 administrator Administrator guest Guest power-user Power User user User 门户系统 Portlet 开发技术解决方案 4.5.1.6. 修改web.xml文件 根据需要添加以下内容: sample-struts-portlet com.liferay.portal.kernel.servlet.PortletContextL istener PortletActionServlet com.liferay.util.bridges.struts.LiferayPortletServ let config /WEB-INF/struts-config.xml TestSessionServlet com.liferay.samplestruts.servlet.TestSessionServle t 1 com.liferay.portal.kernel.servlet.PortletServlet portlet-class org.apache.portals.bridges.struts.StrutsPortlet 0 门户系统 Portlet 开发技术解决方案 PortletActionServlet /portlet_action/* TestSessionServlet /test_session/* 1 /1/* http://struts.apache.org/tags-bean /WEB-INF/tld/struts-bean.tld http://portals.apache.org/bridges/struts/tags-portlet -html /WEB-INF/tld/struts-portlet-html.tld http://struts.apache.org/tags-logic /WEB-INF/tld/struts-logic.tld http://struts.apache.org/tags-nested /WEB-INF/tld/struts-nested.tld http://struts.apache.org/tags-tiles /WEB-INF/tld/struts-tiles.tld http://java.sun.com/portlet_2_0 门户系统 Portlet 开发技术解决方案 /WEB-INF/tld/liferay-portlet.tld http://liferay.com/tld/portlet /WEB-INF/tld/liferay-portlet-ext.tld http://liferay.com/tld/security /WEB-INF/tld/liferay-security.tld http://liferay.com/tld/theme /WEB-INF/tld/liferay-theme.tld http://liferay.com/tld/ui /WEB-INF/tld/liferay-ui.tld http://liferay.com/tld/util /WEB-INF/tld/liferay-util.tld 添加标签库应用 http://struts.apache.org/tags-bean /WEB-INF/tld/struts-bean.tld http://portals.apache.org/bridges/struts/tags-portlet -html /WEB-INF/tld/struts-portlet-html.tld 门户系统 Portlet 开发技术解决方案 http://struts.apache.org/tags-logic /WEB-INF/tld/struts-logic.tld http://struts.apache.org/tags-nested /WEB-INF/tld/struts-nested.tld http://struts.apache.org/tags-tiles /WEB-INF/tld/struts-tiles.tld http://java.sun.com/portlet_2_0 /WEB-INF/tld/liferay-portlet.tld http://liferay.com/tld/portlet /WEB-INF/tld/liferay-portlet-ext.tld http://liferay.com/tld/security /WEB-INF/tld/liferay-security.tld http://liferay.com/tld/theme /WEB-INF/tld/liferay-theme.tld http://liferay.com/tld/ui /WEB-INF/tld/liferay-ui.tld 门户系统 Portlet 开发技术解决方案 http://liferay.com/tld/util /WEB-INF/tld/liferay-util.tld 其他的可在项目中根据需要看是否要增加。 4.5.1.7. 增加context.xml文件 在docroot下面增加META-INF/context.xml,文件内容如下: 作用:让当前的 portlet 依赖于 lifeay 的包。如果没有这个会报 castException 之类的错误,两个应用虽然引用的是同一个包,但是包不在同一个 lib 下 面 ,类 型转换时会认为他们不是同一类型, 门户系统 Portlet 开发技术解决方案 4.5.1.8. 引用外部JAR 外部应用的 JAR 包应放到/WEB-INF/lib 下: 自定义 portlet 工程,如果要引用包,不要放在 WIN-INF/lib 下面,放在 sdk 下面的 lib 下面,如果用到了除了 lifeay 之外的包可以放在 WIN-INF/lib 下面, 因为 deploy 后他会把 WIN-INF/lib 下所有的包都部署过去,而有些包是只能加 载一次的。 门户系统 Portlet 开发技术解决方案 4.5.1.9. 添加类文件 将类文件放到/WEB/INF 下的 SRC 中(注意最好要创建包): 4.5.1.10. 4.5.1.11. 添加标签库 将要添加的标签放到/WEB-INF/tld 中: 门户系统 Portlet 开发技术解决方案 4.5.1.12. 添加JSP文件 将用到的 JSP 文件放到相应位置即可,注意要和 titles-def.xml 文件对应上: 这里以 D:\sample-struts-portlet\html\portlet\sample_struts_portlet 目 录为例说明: 在 titles-def.xml 文件中的定义: 工程中定义: /portlet/sample_struts_portlet/edit.jsp 门户系统 Portlet 开发技术解决方案 4.5.1.12.1. 添加init.jsp文件 添加该文件主要是使用 Liferay 的类库和标签库: <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <%@ taglib uri="http://portals.apache.org/bridges/struts/tags-portlet-html" prefix="html" %> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %> <%@ taglib uri="http://struts.apache.org/tags-nested" prefix="nested" %> <%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %> <%@ page import="java.util.Enumeration" %> <%@ page import="java.util.HashMap" %> <%@ page import="java.util.Iterator" %> <%@ page import="java.util.Locale" %> <%@ page import="java.util.Map" %> <%@ page import="java.util.ResourceBundle" %> 其它页面必须包含该页面: view.jsp 的片段: 门户系统 Portlet 开发技术解决方案 4.5.1.13. 添加脚本扩展 在/WEB/INF 目录下可以添加 CSS 目录和 JS 目录进行脚本扩展。 4.5.1.14. 添加Portlet到Liferay目录 修改 liferay-display.xml 文件中内容: 修改此处内容例如:“category.test” 你将会能在”Test”目录中选择你的 portlet. 门户系统 Portlet 开发技术解决方案 4.5.2. 部署Portlet 采用 ANT 命令部署即可:在 Portlet 目录下输入“ant deploy” 即: 显示“BUILD SUCCESSFUL”表示部署成功,启动 Liferay 验证即可: 门户系统 Portlet 开发技术解决方案
还剩199页未读

继续阅读

pdf贡献者

海盗大哥

贡献于2011-11-08

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf