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的客製化設定。
參考資料:
全站熱搜