close
Liferay提供了Staging機制讓我們可以很輕鬆地將內容同步到Production Server,但是如果是客製化Portlet及Entity就得自行處理這部分的程式撰寫。
 
在Liferay 6.1版本中提供了PortletDataHandler這個類別讓我們可以在其中撰寫我們所需要匯入的程式邏輯,基本上是以匯出/匯入Lar檔案的方式再進行擴充,而在Liferay 6.2版本又多衍伸出StagedModelHandler這個類別,讓我們可以針對各個要匯入的資料分門別類易於管理。
 
附件為Fansysoft-Lar-Simple.portlet,包含一個News資料的Crud以及自訂權限Model設定,在這個基礎之上加入Staging同步功能客製化:
 
* 首先必須修改Service.xml,一個要做Staging的Entity需要設定uuid為true,讓liferay可以讓我們產生Uuid,另外還需要companyId、createDate以及modifiedDate,group也可選擇性添加,如下參考:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">
<service-builder package-path="com.fansysoft.simple">
     <author>Johnny Liu</author>
     <namespace>Fansysoft</namespace>
     <entity name="News" uuid="true" local-service="true" remote-service="false">
           <!-- PK fields -->
           <column name="newsId" type="long" primary="true" />
           <!-- Audit fields -->
           <column name="companyId" type="long"></column>
           <column name="groupId" type="long" />
           <column name="title" type="String" />
           <column name="content" type="String" />
           <column name="userName" type="String" />
           <column name="createDate" type="Date" />
           <column name="modifiedDate" type="Date" />
           <!-- Order -->
           <order by="asc">
                <order-column name="newsId" />
           </order>
           <!-- Finder methods -->
           <finder name="titleAndUsername" return-type="Collection">
                <finder-column name="title" />
                <finder-column name="userName" />
           </finder>
     </entity>
</service-builder>
 
修改完之後重新Build Service,Liferay會自動幫我們產生用來處理Staging的class檔案。
接著建立一個package,這裡為com.fansysoft.simple.lar,裡面建立兩個檔案,分別為NewsDataHandler.java以及NewsStagedModelHandlerImpl.java。
 
NewsDataHandler.java需要繼承BasePortletDataHandler,內容如下:
 
/**
 * @author Johnny Liu
 *
 */
package com.fansysoft.simple.lar;
import java.util.List;
import javax.portlet.PortletPreferences;
import com.fansysoft.simple.model.News;
import com.fansysoft.simple.service.persistence.NewsExportActionableDynamicQuery;
import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
import com.liferay.portal.kernel.lar.BasePortletDataHandler;
import com.liferay.portal.kernel.lar.PortletDataContext;
import com.liferay.portal.kernel.lar.PortletDataHandlerBoolean;
import com.liferay.portal.kernel.lar.StagedModelDataHandlerUtil;
import com.liferay.portal.kernel.lar.StagedModelType;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.xml.Element;
     
/* Liferay 6.2將Portlet Data Handler定義成一個匯出匯入彙整用的class,
 * 需要搭配staged model handler class來使用,用來匯出Portlet底下各個不同的entity及其他資料。
 */
public class NewsDataHandler extends BasePortletDataHandler {
     
     //前台publish畫面取得是否匯出該類型的資料用,建議與service.xml設定的一致
     public static final String NAMESPACE = "fansysoft";
     
     //自定義權限resource name
     protected static final String RESOURCE_NAME = "News";
     
     protected static final String PORTLET_RESOURCE_NAME = News.class.getName();
     
     //建構子用來做初始化設定,例如匯出控制等
     //StagedModelTypes及ExportControls可設定多個,用來作為匯出的資料關聯。
     public NewsDataHandler() {
           setDataLocalized(true);
           setDeletionSystemEventStagedModelTypes(
                new StagedModelType(News.class));
           setExportControls(
                new PortletDataHandlerBoolean(
                      NAMESPACE, "news", true, false, null, News.class.getName())
           );
     }
     //用來取得需要 匯出/同步 的資料數量。
     protected void doPrepareManifestSummary(
                PortletDataContext portletDataContext,
                PortletPreferences portletPreferences)
           throws Exception {
           ActionableDynamicQuery newsActionableDynamicQuery =
                      new NewsExportActionableDynamicQuery(portletDataContext);
           //算筆數的方法。
           newsActionableDynamicQuery.performCount();
     }
     
     //同步時有資料須刪除時所使用的方法
     protected PortletPreferences doDeleteData(
                PortletDataContext portletDataContext, String portletId,
                PortletPreferences portletPreferences)
           throws Exception {
           
           if (portletDataContext.addPrimaryKey(
                      NewsDataHandler.class, "deleteData")) {
                return portletPreferences;
           }
           return portletPreferences;
     }    
     
     //匯出lar檔案/staging同步時使用
     protected String doExportData(
                PortletDataContext portletDataContext, String portletId,
                PortletPreferences portletPreferences)
           throws Exception {
           //取得portlet resource permission並匯出
          portletDataContext.addPortletPermissions(PORTLET_RESOURCE_NAME);
           
           //取得要匯出的portlet內資料的Element,準備作為標籤存入
           Element rootElement = addExportDataRootElement(portletDataContext);
           //如果在前台有勾選要匯出News資料,就執行Staged Model Handler裡的方法
           if (portletDataContext.getBooleanParameter(NAMESPACE, "news")) {
                ActionableDynamicQuery newsActionableDynamicQuery =
                           new NewsExportActionableDynamicQuery(portletDataContext);
                //執行Staged Model Handler裡的Export方法
                newsActionableDynamicQuery.performActions();
                
           }
                
           return getExportDataRootElementString(rootElement);
     }
     //匯入lar檔案/staging同步時使用
     protected PortletPreferences doImportData(
                PortletDataContext portletDataContext, String portletId,
                PortletPreferences portletPreferences, String data)
           throws Exception {
           //匯入portlet resource權限
          portletDataContext.importPortletPermissions(PORTLET_RESOURCE_NAME);
           //_log.error(portletDataContext.getPermissions());
           
           if (portletDataContext.getBooleanParameter(NAMESPACE, "news")) {
                
                //取得要匯入的資料元素
                Element newsElement =
                           portletDataContext.getImportDataGroupElement(News.class);
                
                List<Element> newsElements = newsElement.elements();
                
                for (Element news : newsElements) {
                      //呼叫stagedmodel的import方法
                     StagedModelDataHandlerUtil.importStagedModel(
                           portletDataContext, news);
                }
                
           }
           return portletPreferences;
     }
     
     
     private final static Log _log = LogFactoryUtil.getLog(NewsDataHandler.class);
}
 
NewsStagedModelHandlerImpl .java則需繼承BaseStagedModelDataHandler<News>,news型別便是我們要處理的Entity,內容如下:
 
package com.fansysoft.simple.lar;
import com.fansysoft.simple.model.News;
import com.fansysoft.simple.service.NewsLocalServiceUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.lar.BaseStagedModelDataHandler;
import com.liferay.portal.kernel.lar.ExportImportPathUtil;
import com.liferay.portal.kernel.lar.PortletDataContext;
import com.liferay.portal.kernel.lar.StagedModelDataHandlerUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.xml.Element;
import com.liferay.portal.service.ServiceContext;
//用來實作每個不同的entity或其他資料的同步及匯出匯入
public class NewsStagedModelHandlerImpl extends BaseStagedModelDataHandler<News>{
     public static final String[] CLASS_NAMES = {News.class.getName()};
     
     //自定義權限resource name
     protected static final String RESOURCE_NAME = "News";
     
     //同步時有資料須刪除時所使用的方法
     @Override
     public void deleteStagedModel(String uuid,
                long groupId, String className, String extraData) throws PortalException, SystemException {
           // TODO Auto-generated method stub
           News news =
                      NewsLocalServiceUtil.
                           fetchNewsByUuidAndGroupId(uuid, groupId);
                if (news != null) {
                      NewsLocalServiceUtil.deleteNews(news);
                }
           
     }
     //匯出資料
     @Override
     protected void doExportStagedModel(PortletDataContext portletDataContext, News news)
                throws Exception {
                Element newsElement =
                     portletDataContext.getExportDataElement(news);
                //取得自訂的權限資料
                portletDataContext.addPermissions(RESOURCE_NAME, news.getNewsId());
                //匯入entity
                portletDataContext.addClassedModel(
                           newsElement,
                     ExportImportPathUtil.getModelPath(news),news);
           
     }
     //匯入資料
     @Override
     protected void doImportStagedModel(PortletDataContext portletDataContext, News news)
                throws Exception {
           //先匯入entity的table及class等資訊
                StagedModelDataHandlerUtil.importReferenceStagedModels(
                      portletDataContext, news, News.class);
                ServiceContext serviceContext = portletDataContext.createServiceContext(news);
                News importedNews = null;
                
                //判斷是否為鏡像複製(也就是有變動時才同步)或是直接整個刪除新增
                if (portletDataContext.isDataStrategyMirror()) {
                      
                      //取得是否已有存在的資料,再判斷是要新增或修改
                      News existingNews =
                           NewsLocalServiceUtil.
                                fetchNewsByUuidAndCompanyId(news.getUuid(), serviceContext.getCompanyId());
                      if (existingNews == null) {
                           serviceContext.setUuid(news.getUuid());
                           importedNews =
                                      NewsLocalServiceUtil.addNews(news);
                      }
                      else {
                           importedNews =
                                NewsLocalServiceUtil.updateNews(news);
                      }
                }
                else {
                      importedNews =
                                NewsLocalServiceUtil.addNews(news);
                }
                //執行匯入及權限匯入
                portletDataContext.importClassedModel(news, importedNews);
                portletDataContext.importPermissions(RESOURCE_NAME, news.getNewsId(), importedNews.getNewsId());
     }
     @Override
     public String[] getClassNames() {
           // TODO Auto-generated method stub
           return CLASS_NAMES;
     }
     private final static Log _log = LogFactoryUtil.getLog(NewsStagedModelHandlerImpl.class);
     
}
 
NewsDataHandler.java以及NewsStagedModelHandlerImpl.java便是在處理我們News這支Entity的資料同步工作,詳細Api說明可參考註解,接著需要在Liferay-portlet.xml設定這兩隻class的關聯,內容如下:
 
<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.2.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_2_0.dtd">
<liferay-portlet-app>
     <portlet>
           <portlet-name>Fansysoft-Lar-Simple</portlet-name>
           <icon>/icon.png</icon>
          <portlet-data-handler-class>com.fansysoft.simple.lar.NewsDataHandler</portlet-data-handler-class>
          <staged-model-data-handler-class>com.fansysoft.simple.lar.NewsStagedModelHandlerImpl</staged-model-data-handler-class>
           <header-portlet-css>/css/main.css</header-portlet-css>
          <footer-portlet-javascript>/js/main.js</footer-portlet-javascript>
          <css-class-wrapper>Fansysoft-Lar-Simple-portlet</css-class-wrapper>
     </portlet>
     <role-mapper>
           <role-name>administrator</role-name>
           <role-link>Administrator</role-link>
     </role-mapper>
     <role-mapper>
           <role-name>guest</role-name>
           <role-link>Guest</role-link>
     </role-mapper>
     <role-mapper>
           <role-name>power-user</role-name>
           <role-link>Power User</role-link>
     </role-mapper>
     <role-mapper>
           <role-name>user</role-name>
           <role-link>User</role-link>
     </role-mapper>
</liferay-portlet-app>
 
portlet-data-handler-class及staged-model-data-handler-class要與剛才所創的兩隻class關聯,如果以上設定正確,就可以進入匯入/匯出或是staging同步畫面看看是否有選項出現:
 
 
 
至此便完成Staging的客製化設定。
 
參考資料:
 
 
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 squall75726 的頭像
    squall75726

    菜鳥工程師的Liferay Portal學習筆記

    squall75726 發表在 痞客邦 留言(1) 人氣()