Liferay has provided an ultimate feature of exporting your data as a file of type lar. Which will contain your data which you would like to export and import to some other place for the same portlet type. Liferay has given this feature for almost all the out of box portlets and also we can implement the same for our custom portlet. Here I am implementing for my custom portlet. Here in this example i am going to show from creating an entity in Service Builder to export it from one site and again importing the data in different site.
Step: 1
Create a service builder project. Define an entity inside your service.xml .
Note:
In Liferay 6.2 onwards for an Entity
Eligible to Export and Import It should contain following Fields:
These fields are mandatory to any Entity to become an staged
Model , If you will add these fields
then your Entity Class will Extend StagedGroupedModel. Then this model will
become eligible for Staging and Export/Import. You can check you classes generated
to confirm your ModelClass is extending StagedGroupedModel or not. Just go to
class named ProductModel you can see
there it will be extending StagedGroupedModel.
And I would like to add that we are folloeing standard approach same as
Liferay does for its out ob box portlet . you can import and export your data
even if it doesn’t have these fields.
|
Here is how our service.xml looks
<?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.liferay.product.slayer">
<author>Md Azaz Ali</author>
<namespace>EI</namespace>
<entity name="Product"
uuid="true" local-service="true"
remote-service="false">
<!-- PK fields -->
<column name="productId"
type="long" primary="true"
/>
<!-- Audit fields -->
<column name="companyId"
type="long" />
<column name="groupId"
type="long" />
<column name="userId"
type="long" />
<column name="userName"
type="String" />
<column name="createDate"
type="Date" />
<column name="modifiedDate"
type="Date" />
<!-- Other fields -->
<column name="productName"
type="String" />
<column name="Price"
type="long" />
<column name="sku"
type="String" />
<!-- Order -->
<order by="asc">
<order-column name="productName"
/>
</order>
</entity>
</service-builder>
|
Step: 3
Creating view.jsp inside folder structure
I have created a form in view.jsp and written a addProduct() method in action class ExportImportPortlet to add Products. Here is view.jsp
<%@page import="javax.portlet.ActionRequest"%>
<%@ taglib
uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib
uri="http://alloy.liferay.com/tld/aui"
prefix="aui"%>
<portlet:defineObjects />
<portlet:actionURL var="addProductURL">
<portlet:param name="<%=ActionRequest.ACTION_NAME
%>"
value="addProduct"/>
</portlet:actionURL>
<aui:form action="<%=addProductURL %>">
<aui:input name="productName"></aui:input>
<aui:input name="sku"></aui:input>
<aui:input name="price"></aui:input>
<aui:input name="submit"
type="submit" value="submit"></aui:input>
</aui:form>
|
Here is my action Class
package com.liferay.product.portlet;
import
com.liferay.portal.kernel.log.Log;
import
com.liferay.portal.kernel.log.LogFactoryUtil;
import
com.liferay.portal.kernel.util.ParamUtil;
import
com.liferay.portal.service.ServiceContext;
import com.liferay.portal.service.ServiceContextFactory;
import
com.liferay.product.slayer.model.Product;
import
com.liferay.product.slayer.service.ProductLocalServiceUtil;
import
com.liferay.util.bridges.mvc.MVCPortlet;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
public class ExportImportPortlet
extends MVCPortlet{
public
void addProduct(ActionRequest actionRequest,
ActionResponse
actionResponse) throws IOException, PortletException {
String
productName = ParamUtil.getString(actionRequest, "productName");
String
sku = ParamUtil.getString(actionRequest, "sku");
long
price = ParamUtil.getLong(actionRequest, "price");
try
{
ServiceContext
serviceContext = ServiceContextFactory.getInstance(Product.class.getName(),
actionRequest);
ProductLocalServiceUtil.addProduct(productName,
sku, price, serviceContext);
}
catch (Exception e) {
_log.error(e);
}
}
Log
_log =
LogFactoryUtil.getLog(ExportImportPortlet.class);
}
|
/**
* Copyright (c) 2000-2013 Liferay, Inc. All
rights reserved.
*
* The contents of this file are subject to
the terms of the Liferay Enterprise
* Subscription License
("License"). You may not use this file except in
* compliance with the License. You can
obtain a copy of the License by
* contacting Liferay, Inc. See the License
for the specific language governing
* permissions and limitations under the
License, including but not limited to
* distribution rights of the Software.
*
*
*
*/
package com.liferay.product.slayer.service.impl;
import
com.liferay.portal.kernel.exception.SystemException;
import
com.liferay.portal.service.ServiceContext;
import
com.liferay.product.slayer.model.Product;
import
com.liferay.product.slayer.service.base.ProductLocalServiceBaseImpl;
import java.util.Date;
/**
* The implementation of the product local
service.
*
* <p>
* All custom service methods should be put
in this class. Whenever methods are added, rerun ServiceBuilder to copy their
definitions into the {@link
com.liferay.product.slayer.service.ProductLocalService} interface.
*
* <p>
* This is a local service. Methods of this
service will not have security checks based on the propagated JAAS
credentials because this service can only be accessed from within the same
VM.
* </p>
*
* @author Md Azaz Ali
* @see
com.liferay.product.slayer.service.base.ProductLocalServiceBaseImpl
* @see
com.liferay.product.slayer.service.ProductLocalServiceUtil
*/
public class ProductLocalServiceImpl
extends ProductLocalServiceBaseImpl {
/*
* NOTE FOR DEVELOPERS:
*
* Never reference this interface directly.
Always use {@link com.liferay.product.slayer.service.ProductLocalServiceUtil}
to access the product local service.
*/
public
Product addProduct(String productName, String sku, long price, ServiceContext
serviceContext) throws SystemException {
Product
product =
productPersistence.create(counterLocalService.increment(Product.class.getName()));
product.setGroupId(serviceContext.getScopeGroupId());
product.setCompanyId(serviceContext.getCompanyId());
product.setUserId(serviceContext.getUserId());
product.setUserName(userLocalService.fetchUser(serviceContext.getUserId()).getFullName());
product.setCreateDate(new
Date());
product.setModifiedDate(new
Date());
product.setProductName(productName);
product.setSku(sku);
product.setPrice(price);
productLocalService.addProduct(product);
return
product;
}
}
|
Step: 4
We will create two sites one named Export Site and add a page.
So add some record like
Now add another site with name Import Site and add a page and drop same portlet there. I am not showing scrrenshot for this . you can take reference from previous one. Now we will see The steps which are responsible for Export and Import Process. Step: 5 Now we have to write a class which will extend BaseStagedModelDataHandler and it will override necessary methods. This class will be responsible for writing object to file and retrieving object from file inside exported lar. Here we have created ProductStagedModelDataHandler .
package com.liferay.product.lar;
import com.liferay.counter.service.CounterLocalServiceUtil;
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.xml.Element;
import
com.liferay.portal.service.ServiceContext;
import
com.liferay.product.slayer.model.Product;
import com.liferay.product.slayer.service.ProductLocalServiceUtil;
import java.util.Date;
public class
ProductStagedModelDataHandler extends
BaseStagedModelDataHandler<Product>{
public
static final String[] CLASS_NAMES = {Product.class.getName()};
@Override
public
void deleteStagedModel(String paramString1, long paramLong,
String
paramString2, String paramString3) throws PortalException,
SystemException
{
}
@Override
protected
void doExportStagedModel(
PortletDataContext
portletDataContext, Product product)
throws
Exception {
Element
productElement = portletDataContext.getExportDataElement(product);
portletDataContext.addClassedModel(productElement,
ExportImportPathUtil.getModelPath(product), product);
}
@Override
protected
void doImportStagedModel(
PortletDataContext
portletDataContext, Product product)
throws
Exception {
ServiceContext
serviceContext = portletDataContext.createServiceContext(product);
if
(portletDataContext.isDataStrategyMirror()) {
Product
existingProduct = ProductLocalServiceUtil.fetchProductByUuidAndGroupId(product.getUuid(),
portletDataContext.getGroupId());
if(existingProduct
== null) {
product.setProductId(CounterLocalServiceUtil.increment(Product.class.getName()));
product.setGroupId(portletDataContext.getGroupId());
product.setCreateDate(new
Date());
product.setModifiedDate(new
Date());
ProductLocalServiceUtil.addProduct(product);
}
else {
existingProduct.setProductName(product.getProductName());
existingProduct.setPrice(product.getPrice());
existingProduct.setSku(product.getSku());
ProductLocalServiceUtil.updateProduct(existingProduct);
}
}
else {
product.setProductId(CounterLocalServiceUtil.increment(Product.class.getName()));
product.setGroupId(portletDataContext.getGroupId());
product.setCreateDate(new
Date());
product.setModifiedDate(new
Date());
ProductLocalServiceUtil.addProduct(product);
}
}
@Override
public
String getDisplayName(Product product) {
return
product.getUuid();
}
@Override
public
String[] getClassNames() {
return
CLASS_NAMES;
}
}
|
Note:
In the above code we have overridden
doExportStagedModel which will recive two parameters portletDataContext and
product. Here portletDataContext contains all necessary methods required to
export an object and product is the object to be exported.
In the above code we have overridden
the method wich is returning array of classNames to be exported and imported.
Now
we have also overrided doImportStagedModel this also recieves same
parameters. Here the product object is the from the lar file it means the
exported one. So in our case as we are just changing the site we can just
change the groupId to the current group and generate new Primarykey and add
it to DB . In case you are importing it to different Server or different
Instance of same Portal then you have to change company id to current
companyId , both groupId and companyId of current place(where we are
importing ) is available in portletDataContext object you can see.
|
Step:
6
Now we have to provide the Entry of
this class in liferay-portlet.xml, keep this tag just below icon tag . Any how
I will provide complete file later.
<staged-model-data-handler-class>com.liferay.product.lar.ProductStagedModelDataHandler</staged-model-data-handler-class>
|
Step: 7
Now we have write another class
which will extend from BasePortletDataHandler
and have to override getExportControls ,
getImportControls , doExportData
and doImportData.
package com.liferay.product.lar;
import
com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
import
com.liferay.portal.kernel.dao.orm.DynamicQuery;
import
com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;
import
com.liferay.portal.kernel.exception.SystemException;
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.PortletDataHandlerControl;
import
com.liferay.portal.kernel.lar.StagedModelDataHandlerUtil;
import
com.liferay.portal.kernel.xml.Element;
import
com.liferay.portlet.dynamicdatalists.model.DDLRecordSet;
import
com.liferay.product.slayer.model.Product;
import com.liferay.product.slayer.service.persistence.ProductExportActionableDynamicQuery;
import java.util.List;
import
javax.portlet.PortletPreferences;
public class ProductPortletDataHandler
extends BasePortletDataHandler {
public
static final String NAMESPACE = "product";
@Override
public
PortletDataHandlerControl[] getExportControls() {
PortletDataHandlerBoolean
products = new PortletDataHandlerBoolean(NAMESPACE, "Products",
true, true);
return
new PortletDataHandlerControl[]{products};
}
@Override
public
PortletDataHandlerControl[] getImportControls() {
return
getExportControls();
}
@Override
protected
String doExportData(PortletDataContext portletDataContext,
String
portletId, PortletPreferences portletPreferences)
throws
Exception {
Element
rootElement = addExportDataRootElement(portletDataContext);
if(portletDataContext.getBooleanParameter(NAMESPACE,
"Products")) {
ActionableDynamicQuery
productActionableDynamicQuery =
getProductActionableDynamicQuery(portletDataContext);
productActionableDynamicQuery.performActions();
}
return
getExportDataRootElementString(rootElement);
}
@Override
protected
PortletPreferences doImportData(
PortletDataContext
portletDataContext, String portletId,
PortletPreferences
portletPreferences, String data)
throws
Exception {
if(portletDataContext.getBooleanParameter(NAMESPACE,
"Products")) {
Element
productsElement =
portletDataContext.getImportDataGroupElement(
Product.class);
List<Element>
productElements = productsElement.elements();
for
(Element productElement : productElements) {
StagedModelDataHandlerUtil.importStagedModel(
portletDataContext,
productElement);
}
}
return
portletPreferences;
}
protected
ActionableDynamicQuery getProductActionableDynamicQuery(final
PortletDataContext portletDataContext) throws SystemException {
return
new ProductExportActionableDynamicQuery(portletDataContext) {
@Override
protected
void addCriteria(DynamicQuery dynamicQuery) {
super.addCriteria(dynamicQuery);
dynamicQuery.add(RestrictionsFactoryUtil.eq("groupId",
portletDataContext.getGroupId()));
}
};
}
}
|
Note:
Here in doExportData
method we are adding a root Element
after that we have created getProductActionableDynamicQuery in which we are adding extra criteria like
we have added groupId as we are going to export/import data between sites and
after that we are calles performActions method which will internally get the
records from db and call the doExportStagedModel in
ProductStagedModelDataHandler for every object.
Now In doImportData method we are
checking if Products are to be imported and getting Elements for product in
which each element represents one Product object . And calling importStagedModel
on ProductStagedModelDataHandler . which will import the Data.
|
Step 8:
Now we have to Provide the entry of
this class in liferay-portlet.xml as below . insert it just above <staged-model-data-handler-class>
tag so our final liferay-portlet.xml will be.
<?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>ExportImport</portlet-name>
<icon>/icon.png</icon>
<portlet-data-handler-class>com.liferay.product.lar.ProductPortletDataHandler</portlet-data-handler-class>
<staged-model-data-handler-class>com.liferay.product.lar.ProductStagedModelDataHandler</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>ExportImport-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>
|
Step: 9
Now we are ready with all necessary
work needed to Export and Import data from custom portlet . Now we will go to Export
Site where we have droped the portlet and added some Data .Now click on gear
icon on right side of portlet and click on Export/Import.
Now in below
you will see following options in Three
different category Application, Content and Permissions. In content section we
have Products and it is checked means it will be exported with the data if you
want to skip means don’t want to export Products then you can click on checkbox
and simply uncheck it.
Now to
export click on export button you will see this. Export is in process…
Now once it is done you will see
this screen.
Now you can
download the file and save it. Now if you will open your lar file using any zip
extractor like 7 Zip you can see your data there. Once you ope n your lar file move
to
\ExportImport-201602070547.portlet.lar\group\10181\portlet\ExportImport_WAR_ExportImportportlet\10184\
|
You will see
two folders portlet-data.xml and
portlet.xml.
You can see
data exported .
Step: 10
Now go to import site where we have
droped same portlet Now here also go to
gear icon and click on Export/Import
Now you will
see success message. So to confirm data is imported just go to DB and check the
records
Here you can
see highlighted records which are imported from lar file . As you can see
groupId and primaryKey is different. So we are done with Export Import Feature
of Liferay. For Further advanced Features you can comment on this .
What is the best way to do if my entity refer to another object ex: a document?
ReplyDeletePlease add uuid to service.xml. Otherwise, the model won't extend StagedGroupedModel.
ReplyDelete