Liferay开发学习(1)
Liferay 6.1開發學習(一):環境搭建
一、資源準備
以下資源內容可以在Liferay的官方網站下載,1-3在http://www.liferay.com/downloads/liferay-portal/available-releases下載,Liferay IDE在http://www.liferay.com/downloads/liferay-projects/liferay-ide處下載。
請將以上資源解壓,推薦在某一個盤下面建立一個Liferay的目錄,將IDE、SDK、Liferay-portal-6.1.1-ce-ga2和源碼后解壓后放到這個目錄下面。
二、配置SDK
打開Liferay IDE,Window-->Preferences-->Liferay-->Installed Plugin SDK,點擊右邊的Add…按鈕,選擇Liferay plugins SDK的解壓目錄,如下圖所示,點擊OK,完成SDK的配置。
三、配置Liferay運行環境(以Tomcat為例)
打開Liferay IDE,Window-->Preferences-->Server-->Runtime Environment,點擊右邊Add…,在彈出的服務器選擇里面選擇Liferay,Inc-->Liferay v6.1 CE (Tomcat 7),請在“Create a new location server”打上勾。
點擊Next,選擇liferay-portal-6.1.1-ce-ga2的解壓目錄,如下圖:
點擊Finish完成Tomcat的配置;或者點擊“Next”,此步驟可以在Liferay source location處選擇Liferay的源碼,或者跳過,推薦關聯一下Liferay的源碼,其他的兩個可選,點擊Finish完成Tomca的配置。
四、運行Liferay
經過上面的三步,在Liferay IDE(Eclipse 4.2)的下方的server面板處,可以看到Liferay 6.1 CE Server,現在點擊運行。稍等片刻,待Tomcat啟動完成后,打開瀏覽器輸入http://localhost:8080/,在Liferay第一次運行的時候會出現一個配置向導(此功能為Liferay 6.1開始新增)。
可以在此配置Liferay門戶名稱、使用語言、管理員的姓名、電子郵件,數據庫的連接信息(先在數據庫里面建立數據庫lportal)等內容。如上圖所示,如果出現“數據庫連接不能建立。請檢查您的連接設置。”請檢查數據庫是否已經建立,或者數據庫名、帳號、密碼等信息是否正確。
靜心等待Liferay的初始化,可能需要幾分鐘時間,當Liferay初始化完成后,會自動跳轉到配置保存成功的頁面。
根據Liferay的提示進行密碼設置、密碼提示問題設置等,然后跳轉到Liferay的主頁。
五、Liferay IDE的其他配置
設置工作區編碼
Liferay的開發環境我們需要工程的編碼為UTF-8,新安裝的Eclipse的默認編碼為GBK,修改方式為WindowàPreferencesàGeneralàWorkspace,在這下面的Text file encoding處選擇other,UTF-8。點擊OK保存。
代碼提示、控制臺緩沖區的大小
可以參考前面的一篇博客:MyEclipse/Eclipse的一些技巧
中文字體偏小
可以參考之前一篇博客:解決eclipse3.7中文字體小
六、Liferay的大概使用介紹
在四的基礎上,在右上角可以看到有一個訪問,點擊可以看到有控制面板、我的公共主頁、我的個人主頁、Liferay(這里顯示的是在配置向導處設置的門戶名稱)。這幾個內容是什么意思呢?
控制面板:這個算是liferay的后臺管理。
我的公共主頁:其他人可以看到的自己的頁面內容,一般放置的如個人博客等展示給其他人看的內容。
我的個人主頁:個人主頁的東西是只有自己可以看到,其他人看不到的,一般放置自己的日程,收集的站點,常用的小工具等等內容,是自己的私有空間。
在控制面板里面主要分為個人信息、站點設置、門戶設置、服務器信息等。如果是一般權限的用戶只能看到個人信息。一般開發的管理功能的頁面均放置在此控制面板處,后面介紹如何將自己開發的Portlet,放置在這里。
現在回到“我的公共頁面”或者“我的個人主頁”。依次介紹一下頂部左邊各按鈕的作用。
編輯控制:打上勾之后顯示portlet的控制按鈕,可以勾上和反選看一下效果。如果勾上仍然沒有出現portlet的控制按鈕,而說明當前登錄的用戶沒有此portlet的控制權限。
管理:管理里面有頁面、頁面布局、站點頁面、站點內容四個菜單。頁面和站點頁點功能類似,差別只在于點進去后的默認位置不同;站點內容和后臺的控制面板處站點的內容一樣。頁面布局是調整當前頁面使用什么樣的布局,如一欄,兩欄,三欄等這些內容。
添加:點擊添加顯示的是可以添加的portlet,默認的只有幾個,點擊更多,可以看到所有的有權限的portlet(不同的登錄用戶,根據權限的不同看到的portlet不同)。可以添加一些看看效果。
上頁內容只是Liferay的一些大概介紹,不熟悉的可以隨便點點,熟悉一下相關內容。
七、其他
1、可以嘗試修改一個Liferay里面的eclipse.ini的里面的JVM內存配置,可以提高eclipse的響應,我將里面的-Xms和-Xmx均調到了768M。
2、可以修改一下Eclipse的代碼提示等,參看http://www.huqiwen.com/2012/06/25/some-skill-about-myeclipse-eclipse/
3、Liferay的學習可以多看看Liferay官方網站的文檔和WIKI,雖然質量不是很可,但也可以學到不少內容。
http://www.liferay.com/documentation/liferay-portal/6.1/development
http://www.liferay.com/community/wiki
Liferay 6.1開發學習(二):創建一個Portlet工程
使用Liferay的SDK創建一個簡單的Portlet,此Portlet不包括業務邏輯、不包括數據庫,只有簡單的頁面展現,用以說明Portlet的開發過程。
一、創建Portlet工程
1、打開Liferay IDE,File-->New-->Liferay Project
2、為Portlet工程取名為Study,點擊完成。或者點擊下一步,默認選擇Liferay MVC。
補充說明:
Portlet:這個是一般做Liferay開發選擇的項目,一般說Liferay插件工程,通常都是指的Portlet工程。他的結構和內容和普通的WEB工程區別不大。
Hook:Liferay的Hook是什么東西呢?這個單詞中文可以翻譯成鉤子。是用于重寫或覆蓋Liferay的一些默認方法或頁面。為開發提供了一種在不直修改Liferay核心源碼的情況下修改Liferay核心功能的方法。
Ext:擴展工程的開發方法是Liferay早期版本推薦的開發方法,在6.x版本之后對ext開發模式逐漸不再推薦,主要使用Portlet的開發方法。擴展開發可以繼承Liferay的Portal的大部分接口方法,而Portlet里面可以使用的接口方法均是Services包里面暴露的API。但是和Liferay的核心工程耦合太大,當Liferay的版本升級時對Ext工程的影響太大,基本需要重新修改。所以一般不推薦使用Ext模式。
Layout:布局模板,Liferay的布局是可以定制的,如一個頁面中是兩欄式還是三欄式,每個欄里面又有幾行等等,可以使用此模式進行快速開發。
Theme:主題包。Liferay IDE提供的可以幫助開發人員、設計人員快速開發Liferay主題包的功能,通過此模式提供的向導工具等,為主題包的開發大大的提供了便利。
Portlet部署
Liferay的開發大量依賴Ant(也可以使用maven),在Liferay的開過程中,編譯、代碼生成、打包、部署等都是基于Ant完成的。
1、Liferay 的IDE在Liferay的portlet開發模式下,ant面板默認是可見的,如果找不到可以通過Window-->Show View-->Ant(如果沒有可以在other里面找到)
2、在ant頁板里面,點擊Add-buildfiles將Build.xml文件添加進來。
3、點擊Study-portlet前面的小三角,在出現的下拉菜單里面,雙擊deploy,等待Liferay完成部署操作。
如果在此時出現如下錯誤:
Task cannot continue because ECJ is not installed.
ECJ was automatically installed. Please rerun your task.
原因是ECJ包沒有找到,ECJ是什么?ECJ, the Eclipse Compiler for Java, is an open source incremental compiler used by the Eclipse JDT. It is an option for Liferay builds and is in many cases faster than Javac or Jikes. The jar for ECJ is included in Liferay release 4.4.0 and later.http://www.liferay.com/community/wiki/-/wiki/Main/ECJ這是官方解釋。大概意思是ECJ是一個編譯優化包,可以提升比Javac和JIKES更好的編譯速度。要想使編譯通過,有兩個兩個方法:
方法一:禁用ECJ。在Liferay的SDK下面找到build.Administrator.properties,此處中間的Administrator名稱不一定是這樣的,具體體系名稱是根據當前系統的用戶名生成的。在里面添加如下內容
javac.compiler=modern
#javac.compiler=org.eclipse.jdt.core.JDTCompilerAdapter
方法二:將ECJ的包添加到ant的路徑里面。ecj.jar包可以在Liferay的工程的源碼包\lib\development下面找到。將此包添加到ant的路徑里面。Windows-->preferences-->Ant-->Runtime,在右邊的Classpath-->Ant Home Entries(Default) -->Add External JARs,將ecj包添加進來。
4、啟動Tomcat。此portlet工程會自動部署。
5、打開http://localhost:8080/,登錄系統。點擊左上角的添加-->更多-->示例,在這里面可以看到我們剛建立的study portlet,點擊添加,可以將此portlet添加到頁面上。
Portlet工程結構
一個Portlet工程的大概結構如下:
1、src:這里存儲Java相關文件包,后面會看到還有一個services包。
2、web.xml:此web.xml和普通的web工程的web.xml文件一樣。
3、build.xml,此文件為ant的構建文件,一般不需要修改繼承自SDK。
4、Liferay-plugin-package.properties,此文件為包工程的元數據信息文件,一般開發不需要關注。
5、portlet.xml:portlet定義描述文件,這個文件是標準的portlet 2.0(JSR 268)規范的文件。在這里定義的信息為portlet的名稱、初始化參數、模式類型、portlet的相關信息、權限等。在此文件里面我們可以看到view-template對應的的是/view.jsp,當我們在Liferay里面添加此portlet后,看到的頁面內容就是view.jsp里面的內容。下面的security-rol-ref定義的是哪些角色擁有此portlet的權限。
6、Liferay-portlet.xml:此文件是liferay擴展的portlet的內容,portlet.xml文件里面是標準的Portlet內容,所有的Portal容器的portlet.xml文件描述、結構都是一樣的。但liferay基于自身平臺的需求,又添加了一個Liferay-portlet.xml來擴展portlet的信息。在這里可以看到角色映射,以及Liferay的一些個性化信息,后面有需要的時候詳細說明。
7、liferay-display.xml:在上一個步驟里面我們在添加portlet的時候,看到study這個portlet是位于示例這個目錄下面的,此文件就是定義相關的portlet是放置在哪個目錄下面顯示的。
說明:
在Liferay里面,portlet的名稱一般不要重復,portlet.xml,liferay-portlet.xml,lifray-display.xml這三個文件之間的關聯就是通過portlet 名稱做標識進行關聯。
Liferay 6.1開發學習(三):Portlet簡述
在上一篇文章里面介紹的那個Portlet是在創建工程時默認創建的Portlet頁面,可以對Portlet有一個大概的認識,但是很難全面。本篇文章對于Liferay中基于MVC的Portlet創建做一個相對詳細的描述。
一、Portlet是什么?
Portlet是基于java的web組件,由portlet容器管理,并由容器處理請求,生產動態內容。Portals使用portlets作為可插拔用戶接口組件,提供信息系統的表示層。作為利用servlets進行web應用編程的下一步,portlets實現了web應用的模塊化和用戶中心化。 portlet規范,即jsr(Java Standardization Request )168/268,是為了實現portal和portlet的互操作。它定義了portlet和portlet容器之間的和約,讓portlet實現個性化、表示和安全的api集。規范還定義了怎樣在portlets應用中打包portlets。(引自百度百科:http://baike.baidu.com/view/58961.htm)
Liferay是portlet規范的實現,對于portlet規范實現的主流產品還有IBM、Oracle等的Portal產品,主流產品的詳細可以參看:http://www.huqiwen.com/2012/06/11/what-is-liferay/?此篇文章中關于Liferay市場地位處的圖片。很以很多時候我們將Liferay稱為一個Portal容器,也就是一個提供Portlet運行的環境。
二、創建一個Portlet
1、基于上一節的Study這個portlet工程的基礎。點擊Liferay IDE上的liferay工具欄,有三個按鈕,點擊中間按鈕,New Liferay Portlet。如下圖,點擊下一步。
Portlet plugin project:這里選擇要創建的portlet屬于哪個插件工程。
Source folder:類的放置位置,一般默認。
Portlet Class:要創建的Portlet的控制類的名稱,可以將此類看作Struts中的Action類。
Java package:包名。
Superclass:選擇要繼承哪個類。一般默認,也就是MVCPortlet。下面這幾個類的關系如下:MVCPortlet是LiferayPortlet的子類,LiferayPortlet是GenericPortlet的子類。在MVCPortlet中封裝了一些方便開發的方法,所以一般使用MVCPortlet,如果有特殊需求可以使用他們的父類。
2、在此步驟看到的是Portlet的相關信息,如模式,JSP位置等,此步驟是portlet.xml文件的可視化編輯,此步驟的所有選項都可以通過編輯portlet.xml完成。點擊下一步。
Porltet Info:這里顯示的是Porltet的名稱,顯示名稱,標題等。一般默認,不需要修改。
Portlet Modes:這里是portlet的模式,View、Edit、Help這三種模式,是portlet規范里面定義的。
Liferay Portlet Modes:看名知義,Liferay Portlet模式。一般默認即可。Portlet默認的三種模式,Liferay認為不能滿足實際的需求,所以又新增了這幾種模式。如果選擇多個模式,就可以在portlet的設置里面看到,可以通過設置進行切換,方便進行一些特殊需求,如:config可以用來開發可配置Porltet,管理員可以為指定的Portlet定制相關的參數等。
JSP folder:JSP的存放位置,一般默認的命名是html/porltet名稱,一般默認即可。html是相對于docroot的,完整路徑是docroot/html/demo/view.jsp。
Create resource bundle file:綁定資源文件,主要是國際化。如果想要porltet的名稱是中文的,測必須使用資源文件,liferay推薦工程中的所有文字描述類的內容都使用資源文件來定義,這樣方便國際化,也能避免出現一些可能的亂碼問題。這里先不選,后面詳講國際化。
3、此步驟是Liferay-portlet.xml和liferay-display.xml的可視化編輯。點擊完成即可。
Icon:此portlet的圖標。
Allow mutiple instaces:是否允許在同一個頁面中有多個porltet的實例,默認為否。
CSS:當前porltet的自定義CSS。一般默認。
JavaScript:當前portlet自定義JS。一般默認。
CSS classname:當前portlet的命名空間,防止CSS和其他porltet沖突。
Category:當前的portlet,顯示在哪個分類下面,這里是Liferay-display.xml文件的可視化編輯。
三、MVCPortlet的簡單使用
可以看到在工程的com.huqiwen.study包下面生成了一個Demo的java文件,如果只是讓portlet顯示/html/demo/view.jsp里面的內容,則不需要在Demo.java文件里面添加內容。如果需要讓view.jsp后從臺初始化一些信息,則需要重寫doView文法。view模式顯示時調用doView方法,edite模式顯示時調用doEdite方法,config模式顯示時調用doConfig方法,依此類推。
在此大部分情況下可以將renderRequest當然HttpServletRequest使用。如果要轉換可以通過PortalUtil.getHttpServletResponse(portletResponse)來進行轉換。
可以使用renderRequest.setAttribute(arg0, arg1)方法,在前臺頁面通過JSTL等進行取值。
這里不詳述,和普通的WEB程序基本一樣。
四、其他
在二里面創建的內容都可以通過修改portlet.xml,liferay-portlet.xml,liferay-display.xml等來進行修改調整。創建向導包含的只是一些最通用的內容,一些高級的參數還需要通過修改porltet.xml和liferay-portlet.xml來完成。
通過向導默認porltet的view展現的JSP命名為view.jsp。此名稱可以在docroot下面修改成相應的內容,實際開發中建議命名中和業務相關的,方便后期查找識別,如用戶的可以命名成userView等,修改名稱的同時需要修改Porltet.xml里面init-param對應的名稱,否則會出現找不到頁面的情況。
Liferay 6.1開發學習(四):Service Builder
一、什么是Service Builder?
Service Builder是Liferay IDE(SDK)提供的一種代碼生成方案,開發人員只需要編輯一個數據庫的實體描述文件,即可根據本XML文件生成Spring層代碼、Hibernate層代碼、SQL、SQL索引創建文件、Spring和hibernate的配置文件等,可以大提高開發人員的效率。簡單說就是根據數據庫描述文件,生成service層和持久化層的代碼,開發人員只需要關注控制層即可。
二、Service Builder的使用
1、首先需要有一個Portlet的插件工程,創建方法,參考http://www.huqiwen.com/2012/09/01/liferay-6-1-development-studey-2-create-portlet-project/
2、打開Liferay IDE,點擊工具欄中Liferay工具的中間按鈕,New Liferay Service Builder,彈出的向導如下,填寫相關信息,點擊完成。
Plugin Project:將用于哪個工程,如果有多個工程,請看仔細。
Service file:數據庫描述文件的文件名,使用向導不可更改(可以手動改,后面詳述)
Package path:包名路徑,這里是定義基礎的包名路徑,建議一般規則為:公司域名+portlet+功能模塊名
Namespace:命名空間,如果沒有指定數據庫的表名,則會在前面添加上此命名空間。
Author:作者名,默認讀取系統變量。
Include sample entiry in new file:在自動生成的xml文件中是否包含示例,如果是新手,建議打勾。
3、編輯service.xml文件。在liferay IDE里面提供了service的開視化編輯。簡單介紹一下主要屬性的含義。
Name:實例名稱,就是通常的JavaBean的名稱。
Local Service:本地服務,一般選中。
Remote Service:遠程服務,用于為生成Web Service做支持,如果不需要不要選中。
Human Name:人性化名稱。一般留空即可,此處的名字用于生成文檔時使用,如果留空使用Name。
Table:數據庫名稱,如果不填寫,使用Name。
Uuid:是否生成一個uuid的字段,具體的根據數據庫設計情況而定。可以使用也可以不使用。推薦使用。
Uuid accessor:是否使用uuid存取實體,根據情況而定。可以使用也可以不使用。推薦使用。
Persistence Class:持久化類的名稱,推薦留空,讓自動生成。
Data source:數據源,一般留空。。
Session Factory:數據庫會話工廠,一般留空。
TX Manager:事務管理,一般留空。
Cache enabled:開啟緩存,推薦選中。
JSON enabled:開啟JSON,生成的代碼中包含實體的JSON序列化和反序列化,根據情況而定。如果不使用JSON,請不要勾選。
4、在Columns里面添加字段,填寫字段名稱,類型等。如果要使用特殊的數據庫名稱,或者字段有其他需求,請點開Columns,在下面逐個字段上修改。Db Name對應的為數據庫的字段名稱,其他一般留空即可。
5、Order,查詢結果根據哪個字段排序列。
6、Finders:查詢方法。service builder默認會生成基本的增刪改查方法,如果有一些根據字段查詢某些數據,返回的可以是一個實體也可以是一個List。
<finder name="userId" return-type="user"><finder-column name="userId" /></finder>這個的意思是生成一個方法叫findyByUserId的方法,傳入的參數是userId,返回的類型是User。(這個例子只是為說明,一般如果userId是主鍵,service builder生成的方法里面,不寫此finder,已經包含此方法)
finder-column這里可以填寫多個參數,return-type,如果是Collection則返回類型是List。
Finders方法,可以根據實際情況進行填寫,對于使用finders仍不能滿足的方法,后面詳解。
7、點擊ant里面servicer-builder按鈕,等待Liferay進行代碼生成。或者將可視化編輯器切換到Diagram,點擊右鍵,service builder。
三、Service Builder生成代碼的使用。
如果是第一次使用Service Builder,請按如下步驟操作。
1、WEB-INF目錄下面找到service目錄,點擊右鍵Build Path-->Use as Source Folder。service包里面主要包括生成的代碼里面的接口方法,方便我們進行跨工程的代碼調用。src里面都 實現包。
2、Service Builder的使用,一般有兩種方法。一種是獲取一個Service。
CmsArticleLocalService cmsArticleServie = CmsArticleLocalServiceUtil.getService();
然后使用此xxService調用相關方法
一種是直接使用xxLocalServiceUtil調用相關方法。大部分情況下使用xxLocalServiceUtil進行方法調用。
3、創建一個modle實例一般使用如,xxLocalServiceUtil.createXX()方法。
四、自定義方法
雖然liferay自動生成的代碼里面包含了常用方法,足以應付一般場景,但是有些特殊場景需要一些特殊的方法,如何添加自定義方法呢?
1、找到src下面和此service builder相關的的xxx.service.impl包,找到xxxLocalServiceImpl的文件,打開在此類里添加相關的自定義方法。
2、如果是要為modle添加自定義方法,請在xxx.modle.impl包里面的,xxImpl文件里面添加自定義方法。
3、修改完成后,再次執行Service Builder,會將這些方法自動的添加到其他相關接口、類里面。Liferay的Service Builder方法一般只允許修改上述兩個類,其他的類一般不要修改,因為修改后如果再次執行service builder會被覆蓋掉。
五、修改字段長度
Liferay自動生成的代,如String字段,默認長度是75。如果有特殊的需求,可以在src/META-INF/portlet-model-hints.xml里面修改相應的字段映射長度。找到相應的實體,字段,如下:
<field name="InfoId" type="String" ><hint name="max-length">300</hint></field>這樣就可以將InfoId的數據庫字段長度定義為300。修改完成重新執行service-builder進行文件生成。
六、其他技巧
1、一個service.xml文件里面可以有多個實體名。
2、如果要建立多個service.xml文件,可以在WEB-IN目錄下面建立一個文件夾,比如叫service-builder用來存儲service.xml文件,不同的service.xml執行不同的命名,如User-Service.xml等。手動建立,不必使用向導。
3、如果按照步驟2操作,有多個service.xml文件,則不能執行ant里面的service-builder文件,因為ant只會尋找web-inf目錄下面的services.xml文件。所以要在xx-service.xml文件里面切換到Diagram編輯模式,點擊右鍵,build Service,這樣會對當前XML文件進行構建。
4、即使使用了2、3的方法,也不要刪除web-inf目錄下面的service.xml文件,可以保留一份,不然最終部署后的web.xml描述文件里會少生成一些內容,導致插件工程不能正常運行。
5、同一個插件工程中,不同的xx-services.xml文件必須使用不同的包名,一個services.xml里面可以有多個實體,但不同的services.xml里面必須使用不同的包名,否則會導致生成的的spring配置文件出現覆蓋情況。
Liferay 6.1開發學習(五):編譯調試修改源碼
Liferay是一個開源的項目,開源項目的好處有兩個,一方面我們可以通過閱讀源碼提高水平,了解一些技術的實現原理,另一方面是如果開源的產品不能滿足我們的實際需求,可以通過修改源碼實現。Liferay CE版可以免費獲取源碼,EE版需要購買過產品才能獲取源碼。
(以下文件雖然是基于Liferay 6.1.1所寫,但Liferay 6.2.0版本同樣適用,方法、過程、需要注意事項等均一致)
一、導入源碼
源碼可以從Liferay的官方網站上下載。http://www.liferay.com/downloads/liferay-portal/available-releases
下載后解壓源碼,可以使用Eclipse的File-->Import-->General-->Existing Projects in to Workspace。將解壓的源碼導入到Eclipse中。
導入源碼后,如果是6.1.1的源碼,可能會出現如下的錯誤:Project 'portal-trunk' is missing required source folder: 'portal-web/test'。此錯誤是說在build path下面找不到test目錄。解決方法有兩個(6.2.0中導入源碼后,也有類似問題,解決方法同下面兩個原理一樣):
1、在portal-web下面建立一個名為test的目錄。
2、打開源碼工程目錄下面的.classpath文件(使用文本編輯器打開,editplus或notepad++),在其中找到<classpathentry excluding="**/.svn/**|.svn/" kind="src" path="portal-web/test"/>(大概在11行),將此行注釋掉或刪除。然后回到Eclipse中刷新工程(選中工程按F5,或在右鍵菜單中選擇刷新)。
二、編譯源碼
既然要調試源碼,首先是需要能夠對源碼進行編譯。Liferay的源碼不是一個普通的Web工程,不能使用普通的方法進行編譯部署。但liferay官方已經提供了相應的ant腳本進行編譯部署,但首先還需要一些簡單的設置。
1、打開liferay的源碼目錄,找到app.server.properties文件。
2、將些文件復制一份,重命名為app.server.{username}.properties的文件,其中的{username}為當前系統的帳號名稱。如我的系統帳號是huqiwen,則將其命名為app.server.huqiwen.properties即可。具體的可以打開CMD,以里面顯示用戶名為準。如下圖:
3、打開此文件,找到app.server.parent.dir=${project.dir}/../bundles。將后面的${project.dir}/../bundles替換為下載的綁定Liferay的Tomcat的路徑,如我的為E:/code/liferay6.1/liferay-portal-6.1.1-ce-ga2,則修改后的地址為:?app.server.parent.dir=E:/code/liferay6.1/liferay-portal-6.1.1-ce-ga2。注意:從地址欄里面復制的地址為正斜杠,請修改成反斜杠。
4、現在Eclipse的ant面板里面點擊,add buildfiles,將portal源碼里面的build.xml文件添加到ant面板里面。
5、點開此build文件,先點擊compile,再點擊deploy,即可將源碼編譯部署到tomcat中。
三、debug源碼
在二的基礎上deploy后,即可對liferay的相關源碼進行編譯調試。如果是第一次進行debug,可能會跳轉到顯示Source not found,此時,點擊下面的“edit source lookup path”,在彈出的對話框里面點擊Add -->Java Project,在這里將我們的Portal-trunk工程勾選上,點擊OK等,即可開始Liferay的源碼調試。
四、常見問題
1、在編譯的時候出現如下錯誤
Task cannot continue because ECJ is not installed.
ECJ was automatically installed. Please rerun your task.
這個問題在前面的博客里面提到過,http://www.huqiwen.com/2012/09/01/liferay-6-1-development-study-2-create-portlet-project/
解決方法為:
原因是ECJ包沒有找到,ECJ是什么?ECJ, the Eclipse Compiler for Java, is an open source incremental compiler used by the Eclipse JDT. It is an option for Liferay builds and is in many cases faster than Javac or Jikes. The jar for ECJ is included in Liferay release 4.4.0 and later.http://www.liferay.com/community/wiki/-/wiki/Main/ECJ這是官方解釋。大概意思是ECJ是一個編譯優化包,可以提升比Javac和JIKES更好的編譯速度。要想使編譯通過,有兩個兩個方法:
方法一:禁用ECJ。在Liferay的SDK下面找到build.Administrator.properties,此處中間的Administrator名稱不一定是這樣的,具體體系名稱是根據當前系統的用戶名生成的。在里面添加如下內容
javac.compiler=modern
#javac.compiler=org.eclipse.jdt.core.JDTCompilerAdapter
方法二:將ECJ的包添加到ant的路徑里面。ecj.jar包可以在Liferay的工程的源碼包\lib\development下面找到。將此包添加到ant的路徑里面。Windows-->preferences-->Ant-->Runtime,在右邊的Classpath-->Ant Home Entries(Default) -->Add External JARs,將ecj包添加進來。
2、在編譯源碼的時候出現:
Please set the environment variable ANT_OPTS to the recommended value of
"-Xmx1024m -XX:MaxPermSize=512m".
這個提示的意思是說當前的JVM參數設置的太小了,不夠liferay編譯使用,需要增加JVM內存的分配,并建議將Xmx參數設置成1024m,MaxPermSize設置成512m。這是因為liferay的源碼比較大,內容比較多,使用了大量的classloader等,需要比較大的內存。
解決方法上面已經給出提示了將環境變量(其實就是JVM的參數)推薦設置成上面的提示。
1)在Liferay工程的源碼里面的build.xml上點擊右鍵-->Run as-->External Tool Configurations
2)在tab標簽里面找到Environment,點擊new,在name里面輸入ANT_OPTS,在value里面輸入-Xmx1024m -XX:MaxPermSize=512m,然后點擊apply。
3)再次運行ant的編譯,即可正常編譯。
3、如果出現類似如下錯誤,請參考上文的“二、編譯源碼”
Tomcat is not installed in E:/liferay/6.2/bundles/tomcat-7.0.42. If you already have Tomcat
installed, make sure the property "${app.server.tomcat.dir}" points to your
Tomcat installation. If you wish to automatically install Tomcat into
Liferay 6.1開發學習(六):國際化
Liferay的開發不建議直接在代碼中使用中文等內容,建議使用國際化的方式,從資源文件中讀取語言等信息,Liferay在平臺對封裝了許多操作資源文件的類,我們只需要按照此規則既可方便的實現國際化。
一、Portlet屬性的國際化
在前面創建的Portlet的時候,portlet的名稱等都是英文的,在添加portlet的時候,顯示的也是英文的,如何將這些信息顯示成中文呢?如將下面Study顯示成中文。
1、檢查portelt.xml的XML文件中的此portlet的信息,是否包含下面的代碼,這個是關鍵,如果沒有手動的添加到portlet-info標簽的上面。如查在portlet的創建向導里面勾選了“Create resource bundle file”,則會自動生成。
<resource-bundle>content/Language</resource-bundle>
2、工程的src目錄下面找到content包,如果沒有則創建,如果有則打開。在里面新建Language_zh_CN.properties文件,我們的portlet中文信息寫在此文件里面。
3、在此文件中寫入“javax.portlet.title=此處寫相應的標題文字”,可以直接寫中文,eclipse的properties編輯器,會自動的轉換成unicode代碼。一般我們只寫title就可以,如果有特別需求,可以加上javax.portlet.keywords,javax.portlet.short-title等信息。
4、現在重新打包部署此工程,再次添加portlet,即可看到portlet的名稱變成了中文。
注意:<resource-bundle>content/Language</resource-bundle>這里的內容定義了,我們存放語言文件的目錄在content目錄下面的Language文件,這里也可以定義成自己喜歡的,只要按照此規則即可。
二、內容的國際化
上面的國際化是portlet屬性信息的國際化,如果是我們想在代碼中使用這些國際化信息呢?比如提示語、按鈕名稱等等。這個地方的方法就和普通的java代碼的國際化一樣,在上面的語言文件中寫入相應的key,在代碼中讀取即可。
1、如我們要將“參數名稱”這個詞國際化,則在Language_zh_CN.properties中寫入:
propertyName=\u53c2\u6570\u540d\u79f0
后面的是參數名稱的unicode碼,我們在編輯器中直接寫入中文,編輯器會自動轉換,如果不能轉換可以使用JDK的命令如:
native2ascii -encoding UTF-8 Language_zh_CN.properties.native Language_zh_CN.properties
進行中文編碼的轉換。
2、在代碼中調用:
JSP中:LanguageUtil.get(pageContext,"propertyName")
JAVA代碼中:LanguageUtil.get(portletRequest.getLocale(), "propertyName")。其中的第一個參數為Local,也可以使用Locale.CHINESE
三、Liferay Portlet屬性國際化的修改
在一中提到的portlet屬性的國際化存在一個問題,也就是我們一個語言文件中只能定義一個portlet的屬性信息,如果我們在一個插件工程中需要國際化多個portlet的屬性信息怎么辦?在Liferay的官方論壇、Wiki里面也有不少人討論此問題,但Liferay的解釋就是不支持!在一個資源文件里面只能寫一個portlet的屬性信息。
個人覺得這一點非常不好,如我的一個插件工程中有十幾個portlet,那么難道需要定義十幾個語言資源文件或者拆分成十幾個Portlet?如果支持下面這樣:
javax.portlet.title.MyPortletTitle = My Portlet Title
如果是這樣的才是很合理的,Liferay平臺里面的Portlet的信息就是這樣的形式國際化的,但是插件里面不支持。Liferay平臺本身是使用的Strtus resource bundle,所以和我們的不太一樣。
既然官方不支持,我們就自己動手改造了,好在Liferay是開源的,我們修改他的源碼自己實現就可以了。
方法如下:
1、參考上一篇文章,http://www.huqiwen.com/2012/09/21/liferay-6-1-development-study-5-compile-debug-deploy/,如果編譯部署關聯Liferay的源碼
2、在源碼里面找到PortletResourceBundle.java這個類(小提示:可以在eclipse中使用快捷鍵ctrl+shift+r來快速定位此文件)。找到其中的handleGetObject方法,修改成如下,下面有注釋,顯示了哪里修改的:
@Overrideprotected Object handleGetObject(String key) {if (key == null) {throw new NullPointerException();} String value = null;if (parent != null) {try {value = parent.getString(key);}catch (MissingResourceException mre) {//hqw 2012.09.09 add beginString titlekey = key.concat(StringPool.PERIOD).concat(_portletInfo.getTitle());if (Validator.isNotNull(titlekey)) {titlekey = titlekey.toLowerCase();}try {value = parent.getString(titlekey);} catch (MissingResourceException mre2) {}//end}} if ((value == null) || (value == ResourceBundleUtil.NULL_VALUE)) {value = _getJavaxPortletString(key);} if ((value == null) && ResourceBundleThreadLocal.isReplace()) {value = ResourceBundleUtil.NULL_VALUE;} return value;}在我debug時,發現liferay只是通過javax.portlet.tiltle這個key值尋找,那我們在給他加上具體的title值就可以了。現在修改后的不但兼容他原來的,而且又支持javax.portlet.title.MyPortletTitle = My Portlet Title。這樣的多好
3、現在使用ant進行deploy源碼,再次啟動tomcat就發現,現在的國際化已經支持我們需要的形式了。
Liferay 6.1開發學習(七):Layout布局模板開發
Liferay提供了layout功能,可以方便的自由布局,用戶或者管理員可以根據選用的布局模板對頁面進行自由布局。Liferay默認中提供了幾種常用的layout模板,如單欄目、雙欄目(20%/80%,30%/70%)等幾種常用的,但是這些布局模板并不能滿足我們實際的需求,一些復雜的布局需要自己開發。
LayoutTPL開發
布局模板是一個tpl文件,本身結構非常簡單。基于Liferay IDE可以進行可視化的、拖動的形式進行布局TPL的開發。方法如下 :
1、在Eclipse的工具條上,點擊New Liferay Project,在plugin type里面選擇Layout,點擊finish。則完成了Layouttpl工程的建立。
2、工程里面對于開發有用的文件主要有四個:
*.tpl:這個文件是定義PC訪問時布局模板的文件。
*.wap.tpl:定義手機訪問時的而已模板文件。
*.png:這個圖片是用于顯示布局模板的縮略圖,方便在選擇模板的時候預覽此模板的樣式。
liferay-layout-templates.xml:此文件位于WEB-INF目錄下面,用于定義此布局模板的元數據信息。
3、通常情況下,我們只需要編輯*.tpl文件即可。如下圖,為我拖出來的一個TPL文件。
4、可以通過鼠標可視化的形式,添加column(列)、row(行)等,通過控制點來顯示列寬的百分比等。
布局模板的發布
主題創建修改完成后,點擊ant中的deploy進行發布即可。
其他說明:
layoutTPL的開發非常簡單。通過Liferay IDE通夠實現快速開發。有一些小技巧
1、可以在一個TPL工程里面建立多個布局模板,只需要在liferay-layout-templates.xml里面對這些文件進行定義描述即可。
2、如果在拖動中不能拖出自己想要的寬度百分比,可以進行source模式,手動的修改這些百分比,為 aui-w24,w后面的數值即是百分比。
3、如果不需要進行移動訪問,*.wap.tpl的內容可以不用管。
4、建議添加*.png這個縮略多,方便在添加切換的時候預覽。
Liferay 6.1開發學習(八):主題開發
Liferay可以為不同的頁面、社區、組織等提供不同的主題。方便實現用戶的個性化配置,同時Liferay提供的IDE方便開發人員進行主題的個性化開發。
Liferay主題的創建
1、在Eclipse的工具條上,點擊New Liferay Project,在plugin type里面選擇Theme,點擊finish。則完成了theme工程的建立。
2、Liferay的主題開發是在他提供的一個模板的基礎上進行修改。我們將個性化的內容放置于_diffs目錄下面,在deploy的時候,liferay會自動的將此文件的內容合并到主題中,并覆蓋原先相應的內容。如果沒有覆蓋的則采用默認的樣式等。
3、liferay的主題主要有四部分組成,CSS、images、js、template。這幾部分中前三部分,不需要特別說明。template里面是使用Velocity編寫的模板,方便我們修改他相應的模板內容。
4、依次說明:
CSS
CSS里面的文件是分類的,如forms.css里面是定義的表單相關的,navigation.css里面是定義的導航相關的,layout.css是定義的布局相關的等等。比如我們需要修改導航的樣式,則在_diffs目錄下面建立css目錄,并將上面的navigation.css復制到_diffs/css/下面。我們在此文件上修改,liferay IDE會自動的編譯覆蓋原先的內容。
如果我們需要定義的CSS內容,不是liferay本身提供的,而是我們自己新建立的樣式,則推薦將此樣式寫在_diffs/css/custom.css里面。
JS
js的內容一般不做修改,也沒有必要在主題包里面定義太多的JS相關的內容。
images
將和自己編輯的主題相關的圖片存于images目錄下面。如果需要在代碼中引用,可以使用
String imgpath = themeDisplay.getPathThemeImages();
這里取到的imgpath,即為images目錄。
templates
模板文件,用于定義常用的portlet、導航、通用的等模板文件。可以根據自己的實際需求進行修改。和普通的Velocity一樣,如果對于Velocity不熟悉的,可以大概了解一下,使用起來和普通的HTML沒有太大的區別。
主題的發布
主題創建修改完成后,點擊ant中的deploy進行發布即可。
其他說明
在_diffs目錄里面只需要復制自己要修改的內容,不需要修改的則不需要復制過來,如下圖:
1、我需要修改layout.css,則只需要從上級的CSS目錄里面將此文件復制過來,在此文件上進行修改。不需要修改navigation.css則不用復制。其他的也類似。
2、圖片,我需要將自定義的圖片保存到common目錄下面,則需要要創建一個comon目錄,在引下面存放自己的文件即可。
3、tumbnail.png文件是存放的主題的縮略圖,方便在切換主題時預覽。
Liferay 6.1開發學習(九):Hook開發簡介
一、Hook是什么
hook是什么在http://www.huqiwen.com/2012/09/01/liferay-6-1-development-study-2-create-portlet-project/里面有一個大概的介紹。Liferay提供hook的目的是減少二次開發的代碼與liferay核心工程的耦合,通過hook我們主要可以主要做以下六件事情:
1、覆蓋事件處理程序:如程序啟動事件(application.startup.events)、登錄注銷事件(login.events.pre、login.events.post)、session的創建銷毀事件等。
2、語言包:比如對liferay的某些翻譯不滿意,需要自定義,則可以使用hook覆蓋liferay的語言包。
3、liferay工程的portlet JSP內容:比如想在頂部的top_bar里面添加內容、想修改liferay的用戶編輯界面、想修改用戶登錄界面等等。
4、覆蓋portal.properties里面的屬性配置:可以在hook工程里面覆蓋一些liferay的portal.properties里面屬性配置,但是請注意,在hook工程里面只能覆蓋部分的屬性設置。不能覆蓋的如果需要可以在root/WEB-INF/classes里面添加portal-ext.properties,這個同可以覆蓋所有的。
5、覆蓋liferay的services里面的方法,如UserLocalService、GroupLocalService、OrganizationLocalService等等。
6、Liferay的Model監聽程序,比如user、group、blog等的創建、更新、查看;插件的部署;集群的消息傳遞等等監聽程序。
二、使用Liferay IDE進行hook開發
1、在liferay IDE中,New Liferay Project-->選擇hook-->輸入project Name,點擊完成,即可創建一個hook工程。
2、在IDE的工具條中,選擇New Liferay hook,選擇我們要創建的hook類型,如下圖:
custom JSp:為自定義的JSP文件
portal properties:覆蓋portal屬性
Services:這個可以可以做兩件事情一是覆蓋事件處理程序;二是覆蓋service類
Lanuage properties:這個覆蓋或者新增國際化的語言文件。
3、以Custom JSPs為例,點擊下一步,如下圖
盡量的使用 Add from Liferay。這樣我們可以在彈出的界面中選擇相應的我們要覆蓋的JSP路徑。點擊完成。
4、現在demo-hook工程目錄下的docroot/custom_jsps下面將出現我們上面選擇的路徑的JSP文件,此文件下的JSP路徑一定要和Liferay root下的待覆蓋的JSP路徑相符。現在就可以編輯此JSP來覆蓋我們想覆蓋的內容,打開此JSP可以看到這里面的內容和portal下面的JSP內容完全一樣,修改想修改的部,點擊ant的deploy即可發布。
5、對于示例中的top_js.jspf文件,可以做什么呢?top_js.jspf此文件是portal的頭部JS的加載程序。比如我要在我的portlet工程里面引用jQuery庫,則可以在top_js.jsp里面將jQuery的庫引進來,這樣在portlet的工程里面不用每個頁面都需要引用jQuery,可以避免一個頁面中多個portlet引用jQuery引起的沖突。引用方法如下
在top_js.jspf的最后部分添加:
<script type="text/javascript" src="/html/js/jquery-1.8.0.min.js"></script>
同時,在custom_jsps/html下面建立js目錄,并將jquery-1.8.0.min.js庫復制到此目錄下面即可。
6、對于portal.properties的覆蓋和語言包的覆蓋類似。Services的覆蓋略有不同,在下一篇文章里面詳述。
Liferay 6.1開發學習(十):在Liferay中使用Ajax
在現在的Web開發中,Ajax的使用非常頻繁,合理的使用Ajax也是提高用戶體驗的一個重要手段,在Liferay中使用Ajax其實非常方便,但是和普通的web程序稍有差異。
一、簡單示例
客戶端代碼編寫
1、在Liferay中創建一個簡單的Portlet頁面,可以取名為ajaxPortlet,具體的創建過程可以參考;http://www.huqiwen.com/2012/09/03/liferay-6-1-development-study-3-portlet-explicate/
2、在liferay中ajax的請求地址要使用portlet:resourceURL,a這樣定義:<portlet:resourceURL var="ajaxUrl"/>,而不是普通請求的portlet:actionURL。
3、編輯view.jsp頁面,在頁面中添加<portlet:resourceURL var="ajaxUrl"/>,引入Jquery的包(使用jquery做ajax請求),然后編寫一個簡單的ajax請求代碼。
function ajaxTest(){$.post('<%=ajaxUrl%>',{p_p_resource_id:'test1'},function(data){$('#message').html(data);})}這個代碼的意思就是請求一下服務端,然后將得到的結果顯示到id為message的html元素中。
4、這樣客戶端的代碼就是編寫完成。具體的詳細代碼參看下面中的附件。
服務端代碼編寫
1、在portlet的控制類中重寫serveResource方法。
2、在此方法中添加相應的處理類,此處的處理方法和普通的servlet一樣,可以使用下面的代碼。
@Overridepublic void serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws IOException, PortletException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = null;try {out = response.getWriter();} catch (IOException e) {e.printStackTrace();}String result = "這是服務端返回的結果信息:";out.println(result);out.flush();out.close();super.serveResource(resourceRequest, resourceResponse);}可以看到其實和普通的servlet處理基本上是一模一樣。
二、多ajax請求
上面的示例雖然簡單,但仔細一想就會發現有一個問題,如果在一個頁面中有多個ajax請求怎么辦?我們要依靠一個resourceID來區分不同的請求,同樣也是分客戶端和服務端。
客戶端
方法1:在resourceURL里面添加一個屬性id,如<portlet:resourceURL var="ajaxUrl2" id="test2"/>
方法2:在rsourceURL里面添加一個參數,如:
<portlet:resourceURL var="ajaxUrl1"><portlet:param name="p_p_resource_id" value="test1"/> </portlet:resourceURL>方法3:在ajax請求時添加相應的參數,如:
function ajaxTest(){$.post('<%=ajaxUrl1%>',{p_p_resource_id:'test1'},function(data){$('#message').html(data);})}方法4:直接在URL里面拼接
服務端
服務端的處理只有一個,取得此resourceID,方法為String resourceID = resourceRequest.getResourceID();然后根據不同的resourceID判斷處理不同的ajax請求。返回和一般的處理一樣。
其他
在處理返回結果時,如果不想使用普通的servlet的處理方式,可以使用liferay的ServletResponseUtil.sendFile()方法,此方法主要是用來處理文件的,但用于處理ajax的請求也非常方便。
demo代碼下載:點擊此處下載
Liferay 6.1開發學習(十一):調度器-定時任務
在liferay的開發中經常需要執行一些定時調度任務,一般情況下在普通的web開發中我們使用quartz來做調度,但是在Liferay中已經對于調度做了一個封裝,方便我們在開發中執行調度任務,liferay中封裝了兩種方法方便我們做調度擴展。
一、基于Portlet的調度
如某個的調度是在某個portlet中使用的,則可以使用一面的方法:
1、? 編寫一個類,實現接口:com.liferay.portal.kernel.messageing.MessageListener。如下圖所示。其中的doReceive()方法是定時執行的內容。
2、? 在liferay-portlet.xml里面注冊這個調度類,添加如下代碼:
<scheduler-entry> <scheduler-event-listener-class> xxx.cmsnewsgather.NewsGatherMessageListener(上面此類的全路徑) </scheduler-event-listener-class><trigger><simple><simple-trigger-value>15</simple-trigger-value><time-unit>minute</time-unit></simple></trigger> </scheduler-entry>scheduler-event-listener-class:里面的類為第一步里面編寫的類
simple-trigger-value:里面為調度周期的數值,time-unit為調度周期的單位。上面的意思為每15分鐘執行一次。此時間也可以從配置文件中讀取。則將此標簽完成<property-key>newsgather.gather.time</property-key>,其中的newsgather.gather.time為屬性文件(portal.properties)里面配置的值
Time-unit:表示周期的周期可以為:day、hour、minute、second、week這幾個單位。
3、? 上面的代碼即完成了一個調度器的開始,內容為每15分鐘執行一次第一步類里面的doReceive()方法。
二、基于servlet的調度
注:此方法適用于lifery6.0.6,不適用于liferay6.1.x
1、同上,編寫一個實現com.liferay.portal.kernel.messageing.MessageListener的類。
2、在相應工程下面的web.xml里面添加servlet的注冊信息,如下:
<servlet><servlet-name>Lucene Servlet</servlet-name>?????? <servlet-class> xx.xx.servlet.xxxServlet </servlet-class><load-on-startup>2</load-on-startup> </servlet>3、在上面的servlet的init(ServletConfig servletConfig)方法里面添加如下內容:
super.init(servletConfig); SchedulerEntry schedulerEntry = new SchedulerEntryImpl();?schedulerEntry.setEventListenerClass(xxxMessageListener.class.getName());schedulerEntry.setTimeUnit(TimeUnit.MINUTE);schedulerEntry.setTriggerType(TriggerType.SIMPLE);schedulerEntry.setTriggerValue(15);?try { SchedulerEngineUtil.schedule( schedulerEntry, PortalClassLoaderUtil.getClassLoader()); }其中的xxxMessageListener為第一步編寫的類。分別在setTimeUnit和setTriggerValue里面設置調度的周期單位和時間。
三、兩者的區別及適用情況
對于方法一是推薦使用的,但是有時候我們可能需要在程序中來設置調度周期,但是方法一需要在liferay-portlet.xml里面將調度的周期和時間就固定了。我們需要做一下變通,如在新聞采集的自動采集的代碼里面就使用的方法一,在liferay工程中的日程管理也是使用了此功能。此方法適用于下面情況
- 需要在程序中設置調度周期
- 但是對于時間的精確度需求不是非常高,可以有一定的誤差
如新聞采集的自動采集,如果我們需要對一個網站進行新聞的自動采集,我們一般將這個周期設置成幾個小時重復執行一次,我們可以在liferay-portlet.xml里面設置成這個調度是10分鐘或者30分鐘執行一次檢查,這個時候我們在doReceive()里面調用的代碼不是具體的執行采集的代碼,而是檢查現在的這個時間是否在下調度周期中,如果是則執行采集,如果不是則跳過。
對于方法二而言,適用于以下情況:
- 需要執行調度的代碼不明確屬于某一個portlet。
- 需要精確控制時間。
只要滿足上面的任何一個條件,都適合使用方法二,方法二里面的調度時間可以從數據庫中讀取也可以從配置文件中讀取。
Liferay 6.1開發學習(十二):文件上傳處理
Liferay中提供了完善的文件處理,從liferay 6.1開始在文件的處理方面,不再區分文檔和圖片,統一為文件媒體庫。在普通的portlet插件工程中,如果想將文件上傳到Liferay的文檔庫中,大的階段可以分為兩個流程:一、在portlet中將上傳的文件取到,二調用Liferay的API將文件上傳到文檔庫中。
(注:這里的文件上傳只講服務端的處理,至于客戶端也就是瀏覽器使用普通的表單文件上傳,還是使用swffileupload或者ajax提交等與服務端沒有關系,處理方法都是一樣的)
一、在portlet中取到上傳的文件
這兩的獲取有兩種方法,一種是普通的fileupload的處理方法,一種是使用liferay的API
1、使用fileupload的處理方法:
此種方法的獲取和在普通的servlet里面使用fileupload的方法一樣,核心代碼如下 :
DiskFileItemFactory factory = new DiskFileItemFactory();//PortletFileUpload upload = new PortletFileUpload(factory); ServletFileUpload upload = new ServletFileUpload(factory);upload.setSizeMax(1024 * 1024 * 200);HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(request);List<FileItem> items = upload.parseRequest(servletRequest);注:也可以使用此行代碼,如果使用了此行則下面的HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(request)就可以不要,在upload.parseRequest里面傳actionRequest。但是此方法在weblogic環境下面會出錯,所以如果要在weblogic下面運行,請使用下面的servletFileUpload。
其他代碼就和在普通的Servlet里面使用fileupload一樣,這里不再多寫。
2、使用Liferay的API獲取上傳文件
推薦使用下面的代碼進行文件上傳。
UploadPortletRequest uploadPortletRequest = PortalUtil.getUploadPortletRequest(actionRequest);String sourceFileName = uploadPortletRequest.getFileName("file");String contentType = uploadPortletRequest.getContentType("file");long size = uploadPortletRequest.getSize("file"); // File file = uploadPortletRequest.getFile("file");InputStream is = uploadPortletRequest.getFileAsStream("file");注:其中的getFileName中的file為<input type="file" name="file">中的name值,具體的可以要看自己的調整。
二、將獲取到的文件上傳到Liferay的文檔庫
1、上傳文件
在portlet中獲取到我們上傳的文件,只算是完成了第一步。取到了上傳的文件,怎么傳到Liferay的文件庫中呢?使用如下的接口
DLAppLocalServiceUtil.addFileEntry(long userId, long repositoryId, long folderId,java.lang.String sourceFileName, java.lang.String mimeType,java.lang.String title, java.lang.String description,java.lang.String changeLog, byte[] bytes,com.liferay.portal.service.ServiceContext serviceContext)
此接口的參數逐個說明:
long userId:上傳此文件的用戶id
long repositoryId:倉庫存儲ID,此ID一般為groupid
long folderId:文件夾ID,可以自己創建,或使用liferay默認的如:DLFileEntryTypeConstants.FILE_ENTRY_TYPE_ID_BASIC_DOCUMENT等,最好是自己根據上傳資源的情況分類,如新聞的存到新聞文件,博客的存成博客文件等
String sourceFileName:上傳文件的源文件名
String mimeType:算是文件類型,可以使用mimeType = MimeTypesUtil.getContentType(fileName);根據文件名獲取。
String title:文件標題,這個與SourceFileName的區別在于,此title是最終顯示在系統上的,可以由用戶輸入,源文件名稱是上傳獲取的文件名稱,不能手動改變。可以留空。
String description:關于此文件的描述,可以留空。
String changeLog:文件修改日志,可以留空。
byte[] bytes:文件的正文件,字節數組。
ServiceContext serviceContext:此類可以通過以下代碼
ServiceContext serviceContext = ServiceContextFactory.getInstance(DLFileEntry.class.getName(), request);
獲取。此對象里面包含了一些環境信息,如groupid,companyid,權限,門戶路徑,當前語言,userId等信息。
在實際應用中可以自己將上面的這個接口再做一層封裝,作為一個公共的文件上傳接口,以供其他需要文件上傳的地方調用,具體的請自行封裝,這里就不再帖我封裝的代碼。
2、獲取文件路徑
上面的文件上傳之后返回的是一個FileEntry的對象,如果獲取到上傳的文件的路徑呢,方法如下,可以定義一個方法,用來返回文件路徑,此方法對圖片、各式文件都是有效。
public static String getFilePath(FileEntry fileEntry) {if (null!=fileEntry) {return "/documents/" + fileEntry.getRepositoryId() + "/" + fileEntry.getFolderId() + "/"+ HttpUtil.encodeURL(HtmlUtil.unescape(fileEntry.getTitle()), true) + "/" + fileEntry.getUuid();}else {//如有需要,此處可以定義一個默認圖片return StringPool.BLANK;}或者下面的這個方法,下面的這個更簡便一些
public static String getFilePath(FileEntry fileEntry) {if (null!=fileEntry) {return "/documents/" + fileEntry.getRepositoryId() + "/" + fileEntry.getUuid();}else {//如有需要,此處可以定義一個默認圖片return StringPool.BLANK;}有時候如果文件是一個圖片,上傳的圖片可以很大,如何獲取一個縮略圖呢?如下:
public static String getSmallImagePath(FileEntry fileEntry){String path = getFilePath(fileEntry);
return path+"?imageThumbnail=1";
}
Liferay 6.1開發學習(十三):彈出層/彈出對話框的使用
在日常的開發中,為了提高用戶體驗,經常會使用彈出層或者彈出框。在Liferay中可以使用AUI的標簽及封裝的JavaScript代碼方便的達到這一目的。針對彈出內容的不同,彈出對話框大體上可以分為三種形式,彈出div層、彈出普通的HTML頁或JSP頁面、彈出portlet。
一、彈出div層
有幾種寫法,本質上是一樣的,一種是普通的JavaScript代碼,一種是使用aui標簽。
1、普通的javascript代碼
<script>function test(){AUI().use('aui-dialog', function(A) {var popup = new A.Dialog({bodyContent: A.one('#webDav').html(),centered: true,destroyOnClose: true,modal: false,title: '彈出層測試',width: 500,buttons: [{label: '確定',handler: function() {alert('點擊了確定');}},{label:'取消',handler:function(){this.close();}}]}).render();}); } </script>代碼看起來有點小復雜,其實很簡單,逐步的說明一下:
var popup:?這是定義當前的彈出層對象。在其他地方可以使用popup.close()關閉當前的對話框。
bodyContent:?A.one('#webDav').html():表示彈出層的正文,這里是彈出一個id為webDav的對象。A.one('#webDav').html()這是aui代碼的寫法(aui其實是YUI的衍生版,所以和YUI的用法基本上一樣),表示選擇id為webDav的對象,取其中的html代碼。
centered:彈出后是否居中,默認為false.
destroyOnClose:是否在半閉時銷毀
modal: 是否模態否,也就是是不是鎖定背景是否讓背景可以編輯,值為true/false,默認為false,為true時背景不可以編輯。
title:彈出層的標題,使用單引號或雙引號。
width:彈出層的寬度,必須為數字,不能用百分比。如果不寫為自適應。
height:彈出層的高度,如果不寫為自適應。
buttons:在彈出層上面定義的按鈕。非必須。我們一般添加確定和取消兩個。多個按鈕之間使用逗號分割,text的內容為按鈕的顯示值,handler為綁定的方法
2、使用aui標簽
<aui:script use="aui-dialog">var create = A.one('#createNew');create.on('click',function(event) {var popup = new A.Dialog({bodyContent: A.one('#webDav').html(),centered: true,modal: true,title: '彈出層測試',width: 500}).render();}); </aui:script>和上面的的基本上差不多,這里是在createNew的對象上綁定了一個click的方法,如果此對象被點擊了,執行彈出層代碼。其他的和上面的一樣。
上面使用的是aui標簽,所以需要在頁中引入
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
3、使用Liferay.provide
此種方法和上面的非常類似,如下:
<aui:script> Liferay.provide(window,'showDialog',function() {var A = AUI();var dialog = new A.Dialog({bodyContent: A.one('#webDav').html(),destroyOnClose: true,modal: false,title: '這是標題',width: 900}).render();},['aui-dialog'] ); </aui:script>寫法和上面的沒有太多的區別,只是寫法的不同。showDialog 這個參數其實就是函數名,在其他地方可以直接調用此方法來彈出下面的彈出層,如果是要傳參數可以直接寫在showDialog(arg)這個里面。上面的的function里面添加此參數。如function(arg),在下面直接使用arg參數即可。
二、彈出一個HTML頁面或者是JSP頁面。
彈出一個頁面的方法和上面的基本上相同,具體看下面的示例。
<aui:script> Liferay.provide(window,'showDialog',function() {var A = AUI();var url= "要彈出的頁面的地址";var dialog = new A.Dialog({destroyOnClose: true,modal: false,title: '這是標題',width: 900}).plug(A.Plugin.IO,{uri: url}).render();},['aui-dialog'] ); </aui:script>不同之處是加了一個plug來顯示待彈出的頁面地址。在dialog里面也可以添加bodyContent屬性,里面可以添加上在彈出的過程的提示語,比“頁面正在加載中請稍候……”,在url加載后將替換上面的內容。
三、彈出一個portlet頁面。
使用二里面的方法也可以彈出一個portlet頁面,但是有時候會不能正常工作,步驟2的彈出的原理還是彈出一個div,在這個div里面加載目標頁面的內容。彈出portlet頁面我們需要彈出一個iframe,這樣就可以使彈出的portlet頁面正常工作。方法如下:
<aui:script>Liferay.provide(window,'showPortletUrl',function(url) {var instance = this;Liferay.Util.openWindow({cache: false,dialog: {align: Liferay.Util.Window.ALIGN_CENTER,after: {render: function(event) {this.set('y', this.get('y') + 50);}},width: 1000},dialogIframe: {id: 'siteManagementIframe',uri: url},title: '管理頁面',uri: url});},['liferay-util-window']); </aui:script>在上面的方法中我是將待彈出的porltet的URL以參數的形式傳進來的,調用的時候方法為:showPortletUrl(url)。dialogIframe中的id為彈出的iframe的id名,uri為待加載的portlet頁面地址。
注意:如果要彈出porltet,請講windowState設為pop_up,也就是LiferayWindowState.POP_UP這個變量。
Liferay 6.1開發學習(十四):在自己的Portlet中使用Liferay的全文檢索
在Liferay中的全文檢索是使用的Lucene,方便我們的對內容進行全文檢索。liferay中對文章、文檔、博客、wiki、留言等實現了全文檢索,如何在我們自己的Portlet中使用Liferay的全文檢索呢?
實例場景如下:
我們自己實現了一個CMS,沒有使用Liferay中的Journal類,比如存放新聞的類叫做CmsArticle。我們現在需要對這個新聞進行全文檢索,可以檢索新聞正文、標題、摘要、作者等,同時可以進行范圍搜索,如只搜索標題、只需要正文或全部等。
一、建立索引類
繼承BaseIndexer類,編寫一個索引類,如叫做CmsArticleIndexer。重寫里面的相關搜索方法。一般需要重寫
getClassNames
getSummary
doDelete
doGetDocument
doReindex
getPortletId
等幾個方法,其他的方法可以根據需要進行重寫。
簡單貼幾個主要的方法的內容:
protected Document doGetDocument(Object obj) throws Exception {CmsArticle article = (CmsArticle)obj; long companyId = article.getCompanyId();long groupId = getParentGroupId(article.getGroupId());long scopeGroupId = article.getGroupId(); ……………………省略相關字段的獲取和上面的一樣 Document document = new DocumentImpl(); document.addUID(PORTLET_ID, groupId, articleId);document.addKeyword(Field.COMPANY_ID, companyId);document.addKeyword(Field.PORTLET_ID, PORTLET_ID);document.addKeyword(Field.GROUP_ID, groupId);document.addKeyword(Field.SCOPE_GROUP_ID, scopeGroupId);document.addKeyword(Field.USER_ID, userId); //將不同的字段添加到索引中document.addDate(Field.MODIFIED_DATE, modifedDate);document.addText(Field.TITLE, title);document.addText(Field.CONTENT, ArticleUtil.getArticleText(content));document.addText(Field.DESCRIPTION, description);document.addText(Field.USER_NAME, userName);document.addKeyword(Field.ENTRY_CLASS_NAME, CmsArticle.class.getName());document.addKeyword(Field.ENTRY_CLASS_PK, articleId); //這個為必須的,否則會搜索不到內容document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);document.addKeyword(Field.TYPE, type); return document;}下面是重建索引的方法,方法很簡單,根據傳入的companyId或者也可以使用groupid,將某個范圍下面的內容進行依次取出添加到索引庫里面,這里使用了分頁,如果數據量較小可以不用分頁,但數據量大時務必分頁,否則會將內存耗盡。
//索引調用的方法 protected void doReindex(String[] ids) throws Exception {long companyId = GetterUtil.getLong(ids[0]); reindexArticles(companyId); } //從傳入的companyId里面依分頁將數據取出添加到索引庫里面 protected void reindexArticles(long companyId) throws Exception {int count = CmsArticleLocalServiceUtil.getCmsArticleCountByCompanyId(companyId,APPROVED_STATUS); int pages = count / Indexer.DEFAULT_INTERVAL; for (int i = 0; i <= pages; i++) {int start = (i * Indexer.DEFAULT_INTERVAL);int end = start + Indexer.DEFAULT_INTERVAL;reindexArticles(companyId, start, end);}} //索引內容,在這里可以對新聞的狀態等進行判斷,如只索引審批或發布過的,未審批的不進行索引 protected void reindexArticles(long companyId, int start, int end)throws Exception { List<CmsArticle> articles = CmsArticleLocalServiceUtil.getCmsArticlesByCompanyId(companyId, APPROVED_STATUS,start, end); if (articles.isEmpty()) {return;} Collection<Document> documents = new ArrayList<Document>(); for (CmsArticle article : articles) {Document document = getDocument(article);documents.add(document);} //更新索引 SearchEngineUtil.updateDocuments(getSearchEngineId(),companyId, documents);}上面貼出來的是一些主要方法,可以根據不同的需求進行調整,不滿足的可以參考Indexer相關類的實現,liferay里面有博客、wiki、文章等索引實現,的都是很好的學習參數資料,方法為在源碼的Indexer類名上按ctrl+T,查看他的相關實現或繼承類。
二、注冊索引類
將上面寫的索引類注冊到portlet中。打開liferay-portlet.xml文件,在相關的portlet中添加
<indexer-class>xxx.xxx.xxx.cmsarticle.search.CmsArticleIndexer</indexer-class>
這樣就將此索引類注冊到portlet中了。
注冊有什么好處呢?
1、可以在控制面板中的更新插件處,對某一個指定的portlet重建索引。
2、在控制面板中當點擊重建所有搜索索引時,可以調用到此索引類進行索引重建立。
3、如果設置了portal在啟動時更新索引,則liferay可以調到此類進行索引更新。
簡而言之主是注冊之后,可以讓Liferay來管理索引,方便的進行統一的索引管理、重建等。
三、搜索
建立完了索引,工作只算完成了一小部分,還有搜索。搜索的過程比建立索引的過程要簡單的多,核心代碼只要兩行。
//實例化索引類 Indexer indexer = IndexerRegistryUtil.getIndexer(CmsArticle.class); //進行搜索 Hits results = indexer.search(searchContext); //從結果里面取Document Document[] docs = results.getDocs();第一步不需要特別說明,就是對相關索引類進行初始化。第二步里面的搜索參數是一個searchContext,這是一個什么東西呢?這是Liferay包裝的一個參數類,可以在里面設置一些常用的搜索參數,如關鍵詞、搜索分頁條件等。我們一般可以這樣使用:
//實例化一個SearchContext SearchContext searchContext = SearchContextFactory.getInstance(request); //start和end是分頁用,如我們只要取前20條,為start=0,end=20 searchContext.setStart(start);searchContext.setEnd(end);searchContext.setKeywords(keyWords);既然有分頁,則需要知道結果的總條數才可以,results.length()返回的就的命中的結果數量,也就是搜索結果的總數。根據start、end、總條數即可進行分頁處理。
完整的搜索處理類核心代碼
Indexer indexer = IndexerRegistryUtil.getIndexer(CmsArticle.class); SearchContext searchContext = SearchContextFactory.getInstance(request); int start = (pageNumber-1) * pageSize;int end = pageNumber * pageSize;//取消搜索權限,liferay的搜索權限是否過濾是根據searchContext里面是否能獲取到的userId來區分的searchContext.setUserId(0);searchContext.setStart(start);searchContext.setEnd(end);searchContext.setKeywords(keyWords); //這里是自定義的搜索范圍,liferay默認是全部,這里是為了實現只搜索標題、只搜索正文等而添加的的searchContext.setAttribute(ArticleSearchConstants.SEARCH_SCOPE, searchScope);int countResult = 0;try {Hits results = indexer.search(searchContext); //取得搜索關鍵詞的分詞詞元String[] terms = results.getQueryTerms();//獲取結果總數countResult = results.getLength();Document[] docs = results.getDocs();for (int i=0;i<docs.length;i++) {Document document = docs[i];String title = document.get(Field.TITLE);//對標題進行關鍵詞高亮String hightLightTitle = StringUtil.highlight(title, terms);//從索引中提取正文片段,如果找不到就取正文的前200個字String snippet = results.getSnippets()[i];String content = snippet;if (Validator.isNull(snippet)) {content = StringUtil.shorten(document.get(Field.CONTENT), 200);}//高亮摘要String higthLightContent = StringUtil.highlight(content, terms);//取新聞的IDString articleId = document.get(Field.ENTRY_CLASS_PK);//取新聞的groupIdlong groupId = GetterUtil.getLong(document.get(Field.GROUP_ID)); //這里可以將數據的數據封裝到相關的搜索結果類里面,進行界面上的顯示,下面的搜索結果類的封裝省略}} catch (SearchException e) {e.printStackTrace();}四、其他說明
1、如何在添加文章或內容時對內容建立索引?
可以另建立一個索引線程,在內容添加的時候將此類添加到索引中,刪除時從索引中刪除。核心的是使用下面的方法。
indexer.reindex(article);
indexer.delete(article);
2、如何重建所有索引?
可以在控制面板中,服務器-->插件安裝-->找到此portlet,旁邊有一個重建索引的按鈕,點擊即可完成對索引的重建。
3、索引文件存儲于哪個地方?
默認存儲于/data/lucene目錄下面
Liferay 6.1開發學習(十五):可配置portlet開發
一、什么是可配置portlet
其實portlet本來就是可以配置的,但我們的開發大部分情況下只使用view模式,edit和config模式一般沒有使用,對于使用editor和config等模式的portlet,我們可以將他們稱為可配置portlet。通過使用可配置portlet,可以做許多個性化定制。
應用場景:
1、如果在首頁上有展現專題的地方,可以建立一個專題展現的portlet,這個地方要展現的內容為一個圖片或多個圖片,點擊圖片可以跳轉相應的鏈接。但是專題可能需要變化,則這里可以添加一個config或edit模式來讓管理員通過配置參數來定制。
2、如首頁的新聞欄目,設計時是展現的A欄目,但是實際中用戶可能有變化,需要換成其他欄目,同樣可以通過可配置portlet來滿足。
3、如提供RSS訂閱,我們可以在配置項里面設置RSS的輸出方法為標題或者是摘要或者是全文,標準可以為atom或者rss2.0等配置。
4、如有一個指標展現,用戶需求為可定制的,用戶A可以選擇柱狀圖、用戶B可以選擇折線圖、用戶C可以選擇餅圖等。
包括但不限于以上場景,需要通過配置來適用不同的情況,為用戶提供可配置選項的地方都可以使用可配置portlet。
可配置portlet的開發方式
可配置portlet的開發方式,我按數據的存儲方式的不同,大概的分為兩種。一種為使用PortletPreferences存儲的,一種為自定義數據表結構存儲的。
使用PortletPreferences存儲的方式為將配置數據以鍵值對的形式存儲于PortletPreferences的相關屬性字段里面。以portlet的實例ID做為識別進行存儲信息,適用于配置信息不算太復雜的場景。
如果配置信息比較復雜,推薦建立相關的數據庫,將配置信息存儲于數據庫中。本文主要介紹存儲于PortletPreferences中的方法,存儲于數據庫和普通的portlet的數據存儲方法類似。
如果按模式的不同,又可以分為edit、config、help等不同的模式。這些對應于建立portlet時選用的modes的不同而不同,這里主要介紹config模式。其他模式類似。
使用Config模式
1、需要有一個建立好的portlet。portlet的創建,參考前面的portlet簡述:http://www.huqiwen.com/2012/09/03/liferay-6-1-development-study-3-portlet-explicate/
2、在Liferay-portlet.xml里面找到此portlet的相關配置,在里面添加configuration-action-class元素,如下:
<portlet><portlet-name>customjspportlet</portlet-name><icon>/icon.png</icon><configuration-action-class>xx.xxx.xxx.customjspportlet.CustomJspConfigurationAction</configuration-action-class><instanceable>true</instanceable><header-portlet-css>/css/main.css</header-portlet-css><footer-portlet-javascript>/js/main.js</footer-portlet-javascript><css-class-wrapper>customjspportlet-portlet</css-class-wrapper></portlet>3、建立CustomJspConfigurationAction類,此類繼承自DefaultConfigurationAction即可。
4、重寫其中的render方法。如下。
public String render(PortletConfig portletConfig, RenderRequest renderRequest, RenderResponse renderResponse){ String portletId = renderRequest.getParameter("portletResource");PortletPreferences preferences = PortletPreferencesFactoryUtil.getPortletSetup(renderRequest, portletId); renderRequest.setAttribute("customjspConfig_page_title",preferences.getValue("customjspConfig_page_title", StringPool.BLANK));renderRequest.setAttribute("customjspConfig_page_link",preferences.getValue("customjspConfig_page_link", StringPool.BLANK)); return "/html/CustomJspPortlet/config.jsp"; }這個方法是點擊portlet中的配置時進入的方法。在這個方法里面做以下幾件事情。
1):我們獲取到了當前portlet的PortletPreferences。
2):從PortletPreferences里面獲取key為customjspConfig_page_title和customjspConfig_page_link的數據,并將他們放到request里面。
3):告訴Liferay我的配置頁的JSP的路徑是哪個。
5、編寫config.jsp頁面。config.jsp頁面里面是我們要配置的一此參數信息,大部分情況下是一個展現的表單。主要內容可以參考如下(從項目中截取的部分代碼,為說明問題已經簡化):
<form action="<liferay-portlet:actionURL portletConfiguration="true" />" name="<portlet:namespace />fm" id="<portlet:namespace />fm" method="post"> <ul><li><span>標題:</span><input tabindex="1" type="text" name="<portlet:namespace />customjspConfig_page_title" id="<portlet:namespace />customjspConfig_page_title" value="<%=title%>" /></li><li><span>鏈接地址:</span><input id='<portlet:namespace />custom_page_link' name='<portlet:namespace />customjspConfig_page_link' type="text" value="<%=link %>" /> </li><li><input type="button" value="" οnclick="<portlet:namespace />saveConfig()"></li> </ul> </form>這里的關鍵點為form的action,所有這種模式的可配置portlet的action都可以固定為:<liferay-portlet:actionURL portletConfiguration="true" />。
6、服務端保存配置信息的處理。步驟5中的action會進入步驟3建立的配置類的processAction方法。在上面的配置類里重寫processAction方法。里面的內容如下:
public void processAction(PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse)throws Exception {String portletResource = ParamUtil.getString(actionRequest, "portletResource"); PortletPreferences preferences = PortletPreferencesFactoryUtil.getPortletSetup(actionRequest, portletResource); if (Validator.isNotNull(preferences)) { //從request里面取數據String title = ParamUtil.getString(actionRequest, "customjspConfig_page_title");String link = ParamUtil.getString(actionRequest, "customjspConfig_page_link"); //將數據以鍵值對的形式填充到preferences里面preferences.setValue("customjspConfig_page_title", title);preferences.setValue("customjspConfig_page_link", link); //存儲數據到數據庫中,持久化數據preferences.store();SessionMessages.add(actionRequest, "success");}super.processAction(portletConfig, actionRequest, actionResponse);}到這里已經完成了可配置portlet的配置部分的開發。
在view.jsp中使用配置數據
前面步驟開發的配置數據的目標是為了在view.jsp中使用,在view.jsp中使用可以在view.jsp的action中使用,也可以直接在view.jsp中直接提取,方法為:
PortletPreferences preferences = renderRequest.getPreferences();String title = preferences.getValue("customjspConfig_page_title", StringPool.BLANK);String link = preferences.getValue("customjspConfig_page_link", StringPool.BLANK);通過這樣的方法即可取到前面的配置信息,取取的數據具體怎么展現,怎么使用,根據不同的業務場景有所不同。
總結
以上是生活随笔為你收集整理的Liferay开发学习(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片饱和度更改,c#,winform
- 下一篇: 安卓rtmp推流app_视频直播app开