Personalization是一个再熟悉不过的词语了,它表示让应用程序用户根据自己的偏好设定来显示用户所希望的内容。Portal框架天生就提供了个人化所需要的数据库表和API,所以无需用户自己去手动建立这些基础。

要实现Personalization 需要以下步骤:

(1)配置Portlet让其支持Edit模式

   
   
   
   
  1. <portlet> 
  2. <portlet-name>bookCatalogportlet-name> 
  3. <portlet-class> 
  4. chapter10.code.listing.base.BookCatalogPortlet 
  5. portlet-class> 
  6. <supports> 
  7. <mime-type>text/htmlmime-type> 
  8. <portlet-mode>viewportlet-mode> 
  9. <portlet-mode>editportlet-mode> 
  10. supports> 
  11. portlet> 

 (2) 为Edit模式写一个render方法,从而可以显示这个Edit模式下的Portlet页面

   
   
   
   
  1. @RenderMode(name = "edit"
  2. public void showPrefs(RenderRequest request, RenderResponse response) 
  3. throws IOException, PortletException { 
  4. PortletRequestDispatcher dispatcher = 
  5. getPortletContext().getRequestDispatcher( 
  6. response.encodeURL("/WEB-INF/jsp/preferences.jsp"
  7. ); 
  8. dispatcher.include(request, response); 

(3)校验需要被持久化的个人偏好,使用PreferencesValidator接口。

我们先定义一个校验器类让其实现PreferencesValidator接口,并且在validator方法中给出校验逻辑(因为只负责校验[SRP原则:单一职责原则],所以这段代码里面千万不能调用store()方法):

   
   
   
   
  1. public class BookCatalogPrefsValidator 
  2. implements PreferencesValidator { 
  3.  
  4. public void validate(PortletPreferences prefs) 
  5. throws ValidatorException { 
  6. BookService bookService = ... 
  7. //从PortletPreferences取得要校验的值
  8. String prefBookISBN = prefs.getValue("prefBookISBN",""); 
  9. //校验取得的值
  10. if(bookService.isUniqueISBN (NumberUtils.toLong(prefBookISBN, -1L)) { 
  11. //如果校验过程发生了规则违背,那么推荐做法是吧这个portlet上产生的key,比如prefBookISBN添加到failedKeys数组中
  12. List failedKeys = new ArrayList(); 
  13. failedKeys.add("prefBookISBN"); 
  14. //然后用这个key作为参数传递到ValidatorException的构造函数中
  15. throw new ValidatorException( "ISBN number does not exist in catalog"failedKeys); 
  16. ... 

然后我们在portlet.xml中进行配置,让我们的目标Portlet使用我们定义的Preferences校验器。

   
   
   
   
  1. <portlet-app ...> 
  2. <portlet> 
  3. <portlet-name>bookCatalogportlet-name> 
  4. <portlet-class> 
  5. code.listing.base.BookCatalogPortlet 
  6. portlet-class> 
  7. ... 
  8. <portlet-preferences> 
  9. <preferences-validator> 
  10. code.listing.validators.BookCatalogPrefsValidator 
  11. preferences-validator> 
  12. portlet-preferences> 
  13. portlet> 
  14. ... 
  15. portlet-app> 

(4)使用PortletPreferences对象来把个人偏好持久化到数据库中,我们无需写任何的数据库访问代码,因为Portlet API已经帮我们封装好了。在prefs.store()的内部,Portlet框架会自动定义我们关联到这个Portlet类的校验类的校验方法。

   
   
   
   
  1. @ProcessAction(name="savePreferences")  
  2. public void savePreferences(...)  
  3. throws PortletException, IOException {  
  4.  
  5. //获取页面上的多值 
  6. String[] prefCategories = request.getParameterValues("prefCategory");  
  7.  
  8. //获取页面上的单值 
  9. String searchTypePref = request.getParameterValue("searchType");  
  10.  //获得一个PortletPreferences对象 
  11. PortletPreferences prefs = request.getPreferences(); 
  12. //将获取的多值属性放到portletPreferences上 
  13. if(prefCategories != null){ 
  14.   prefs.setValues("category",prefCategories); 
  15. //将获取的单值属性放到portletPreferences上 
  16. if(searchTypePref != null && !searchTypePref.equals("-1")){ 
  17.   prefs.setValue("searchType",searchTypePref); 
  18.  .. 
  19. //持久化“已经”被放在PortletPreferences上的对象 
  20. try{
  21. prefs.store(); 
  22. }catch(ValidatorException ex) {
  23. //进行异常处理,比如显示所有失败的key到页面上 Enumeration failedKey = ex.getFailedKeys(); request.setAttribute("failedPrefs",failedKeys); }
  24.  
  25. }  

 

(5)用PortletPreferences对象来获取个人偏好,并且基于偏好来定制Portlet的内容和行为。

首先一点是,必须知道Preferences的值来自哪里,比如,对于以下取得Preferences上的value1值,

   
   
   
   
  1. @RenderMode(name = "edit"
  2. public void showPrefs(RenderRequest request, RenderResponse response) 
  3. throws IOException, PortletException { 
  4. //取得Preferences对象
  5. PortletPreferences prefs = request.getPreferences(); 
  6. //取得Preferences对象上的值
  7. String value = prefs.getValue("pref1""-99"); 
  8. ... 

有如下规则:

a.如果这个key被放到持久存储中,那么prefs.getValue()取得的值是持久化了的值。

b.如果这个key没有被放到持久存储中,(或者放到了持久存储中但是无法被访问)而portlet.xml定义了这个key的初始化值,那么prefs.getValue()取得的就是portlet.xml上定义的初始化值,如下面的代码片段:

   
   
   
   
  1. <portlet> 
  2. <portlet-name>bookCatalogportlet-name> 
  3. ... 
  4. <portlet-preferences> 
  5. <preference> 
  6. <name>maxNumOfBooksname> 
  7. <value>1000value> 
  8. preference> 
  9. <preference> 
  10. <name>greetingMessagename> 
  11. <value>Hello !value> 
  12. <read-only>trueread-only> 
  13. preference> 
  14. ... 
  15.  
  16. portlet-preferences> 
  17. portlet> 

c.如果key既没有被放到持久存储,(或者被放了但是无法被访问),同时portlet.xml中也没有定义这个key的初始化值,那么prefs.getValue()就是取得backup的值,也就是本例中prefs.getValue("pref1","-99")中的-99.

 

知道了peferences的值来自哪里之后,我们可以在页面上使用个人化的值,我们利用页面上的脚本变量比如portletPreferences 和 portletPreferencesValues,或者直接在scriptlet中编程的使用API.

   
   
   
   
  1. <portlet:definedObjects/> 
  2. ... 
  3. <form ...> 
  4. <
  5. java.util.List catList = java.util.Arrays.asList 
  6. (portletPreferences.getValues("category", 
  7. new String[] {"-1"})); 
  8. %> 
  9. ... 
  10. <td><b>Preferred Categoryb>td> 
  11. <td> 
  12. <select name="category" 
  13. multiple="multiple"> 
  14. <option value="java" <%=catList.contains("java") ? "selected" : ""%>>Javaoption> 
  15. <option value=".net" <%=catList.contains(".net") ? "selected" : ""%>>.NEToption> 
  16. ... 
  17. td> 
  18. ... 
  19. <td><b>Greetings Messageb>td> 
  20. <td> 
  21. ${portletPreferencesValues.greetingMessage[0]} 
  22. <i><username>i>
  23. td> 
  24. ... 
  25. form>