public class ExpenseClaim extends PersistenceEntityBase implements NormativeId, CreatorAware,
EntityImportSupport, EntityExportSupport {
}
The entity import and/or export menu nodes will be added to entityType context menu
and its EntityListBackingBean header or footer menu.
@Override
protected Element createEntityElement(ExpenseClaim entity, Document doc) throws SystemException {
Element entityElem = doc.createElement("expenseClaim");
entityElem.setAttribute("nid", entity.getNid());
XmlUtil.setChildElementTextValue(entityElem, "summary", entity.getSummary());
...
return entityElem;
}
Accordingly override the XML decoding by overriding the following method
in its EntityBackingBean (ExpenseClaimBean).
@Override
protected void fillEntityData(Element entityElem, ExpenseClaim entity,
Map<EntityTypeId, PersistenceEntity> entityRemap) throws SystemException {
String nid = entityElem.getAttribute("nid");
String summary = XmlUtil.getChildElementTextContent(entityElem, "summary");
entity.setNid(nid);
entity.setSummary(summary);
...
}
moduleRootDir/ src/ |-- main/ |-- java/ |-- resources |-- import/ System.FD/ |-- 1001.xml |-- 1002.xml |-- index.xmlTo enable import from system (module) for an entity type, override the following method in its EntityBackingBean:
@Override
public SystemEntityImportDescriptor getSystemEntityImportDescriptor(
TypedMenuNodeFactory factory) {
return new SystemEntityImportDescriptor(true, null);
}
Entity data files for import will be packaged inside module jar under the following path:
<moduleName>/import/<dataType>For example, ExpenseClaim form design XMLs of HR module will be packaged inside jar as followings.
HR/import/System.FD/1001.xml
HR/import/System.FD/1002.xml
HR/import/System.FD/index.xml
"System.FD" is the dataType of FormDesign in System module. FormDesign entity type is
defined in System module, but other modules can provide their own FormDesign XMLs for
import. The module name can be omitted for entity types defined in its own module.
For example, if HR module defines AreaCode(Country/State/City) for import, and the
dataType for AreaCode is HR.AC.
moduleRootDir/ src/ |-- main/ |-- java/ |-- resources |-- import/ System.FD/ |-- 1001.xml |-- 1002.xml |-- index.xml AC/ |-- united_states.xml |-- canada.xml |-- index.xmlThe index.xml lists all the entity data files available for import.
<?xml version="1.0" encoding="UTF-8"?>
<dataSources>
<dataSource id="1001">
<name>Expense Claim - Classic</name>
<file>1001.xml</file>
</dataSource>
<dataSource id="1002">
<name>Expense Claim - Modern</name>
<file>1002.xml</file>
</dataSource>
</dataSources>
HR/import/System.FD/1001.xml
HR/import/System.FD/1002.xml
HR/import/System.FD/index.xml
HR/import/System.FD/fr/1001.xml
HR/import/System.FD/fr/1002.xml
HR/import/System.FD/fr/index.xml
HR/import/System.FD/zh/1001.xml
HR/import/System.FD/zh/1002.xml
HR/import/System.FD/zh/index.xml
Take HR module for instance. Export the addresses and expenseClaims of an employee as zip. Assume that Address and ExpenseClaim implement EntityImportSupport and EntityExportSupport, and Employee implements EntityImportSupport. Add an Export menu node in EmployeeBean to export data for the current employee. If Employee implements EntityExportSupport, the Export context menu node would export all the employees in one file.
In EmployeeBean,Override export/import file name extension,
@Override
public String getFileNameExtension() {
return "zip";
}
Override conflict action choices,
@Override
public List<SelectItem> getConflictActionSelectItems() throws SystemException {
// create only
Set<Object> excludeValues = new HashSet<Object>();
excludeValues.add(ConflictAction.ABORT);
excludeValues.add(ConflictAction.OVERRIDE);
excludeValues.add(ConflictAction.SKIP);
return SelectItemListProvider.getInstance(null).getSelectItems(
SelectItemListProvider.CONFLICT_ACTION, excludeValues, null, true,
false);
}
To handle Export menu node action to export an employee data to zip,
@Override
public PageNavigation clickMenuNode(MenuNode menuNode) throws BackingBeanException,
SystemException {
String command = menuNode.getCommand();
Employee employee = this.getEntity();
if (command.equals("Export")) {
File zipFile = ; // temporary file
exportEmployeeDataAsZip(employee, zipFile);
// download
DownloadHelper downloadHelper = new DownloadHelper("application/zip", null,
employee.getName());
OutputStream os = downloadHelper.getOutputStream();
InputStream is = null;
try {
is = new FileInputStream(zipFile);
FileUtil.copy(is, os);
downloadHelper.close();
} catch (IOException e) {
throw new BackingBeanException(e);
} finally {
try {
if (is != null)
is.close();
} catch (Throwable t) {
// log error
}
zipFile.delete();
}
return null;
}
return super.clickMenuNode(menuNode);
}
public void exportEmployeeDataAsZip(Employee employee, File zipFile) throws SystemException {
try {
File tempDir = new File(...); // create temporary directory
/**
* zip:
*
* addresses.xml
* employee.xml
* expenseClaims.xml
*/
BackingBeanContext context = BackingBeanContext.getInstance();
User user = employee.getUser();
// addresses
File addressesXmlFile = File.createTempFile(null, ".xml", tempDir);
QueryCriteria<Address, Address> queryCriteria = new QueryCriteria<Address, Address>(Address.class,
new CriteriaElement[]{DetachedCriteria.eq("user", user),
DetachedCriteria.asc("id")});
EntityBackingBean<Address> addressBean = context.getEntityBackingBean(Address.class, null);
addressBean.exportEntitiesToFile(queryCriteria, addressesXmlFile);
// employee
File employeeXmlFile = File.createTempFile(null, ".xml", tempDir);
QueryCriteria<Employee, Employee> queryCriteria = new QueryCriteria<Employee, Employee>(Employee.class,
new CriteriaElement[]{DetachedCriteria.eq("id", employee.getId())});
EntityBackingBean<Employee> employeeBean = context.getEntityBackingBean(Employee.class, null);
employeeBean.exportEntitiesToFile(queryCriteria, employeeXmlFile);
// expenseClaims
File expenseClaimsXmlFile = File.createTempFile(null, ".xml", tempDir);
QueryCriteria<ExpenseClaim, ExpenseClaim> queryCriteria = new QueryCriteria<ExpenseClaim, ExpenseClaim>(ExpenseClaim.class,
new CriteriaElement[]{DetachedCriteria.eq("employee", employee),
DetachedCriteria.asc("nid")});
EntityBackingBean<ExpenseClaim> expenseClaimBean = context.getEntityBackingBean(ExpenseClaim.class, null);
expenseClaimBean.exportEntitiesToFile(queryCriteria, expenseClaimsXmlFile);
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
ZipUtil.addToZip(addressesXmlFile, "addresses.xml", zos);
ZipUtil.addToZip(employeeXmlFile, "employee.xml", zos);
ZipUtil.addToZip(expenseClaimsXmlFile, "expenseClaims.xml", zos);
zos.close();
// delete temporary files
addressXmlFile.delete();
employeeXmlFile.delete();
expenseClaimXmlFile.delete();
} catch (Exception e) {
throw new SystemException(e);
}
}
The following method will be called from Employee context menu to
import the addresses and expenseClaims of an employee from a zip:
@Override
public HandleResult importEntities(EntityImportDataSource dataSource,
ConflictAction conflictAction, Map<EntityTypeId, PersistenceEntity> entityRemap) throws SystemException {
File tempDir = new File(...);// create a temporary directory
try {
InputStream zipFileInputStream = dataSource.getInputStream();
ZipUtil.unzip(zipFileInputStream, tempDir, null);
/**
* zip:
*
* addresses.xml
* employee.xml
* expenseClaims.xml
*/
Map<EntityTypeId, PersistenceEntity> entityRemap = new HashMap<EntityTypeId, PersistenceEntity>();
BackingBeanContext context = BackingBeanContext.getInstance();
HandleResult totalResults = new HandleResult(0, 0 0);
// addresses
File addressesXmlFile = new File(tempDir, "addresses.xml");
if (addressesXmlFile.exists()) {
EntityBackingBean<Address> addressBean = context.getEntityBackingBean(Address.class, containerBean);
EntityImportDataSource dataSource = new FileDataSource("HR", addressesXmlFile);
HandleResult result = addressBean.importEntities(dataSource, ConflictAction.CREATE_NEW, entityRemap);
totalResults.add(result);
}
// employee
File employeeXmlFile = new File(tempDir, "employee.xml");
if (employeeXmlFile.exists()) {
EntityBackingBean<Address> employeeBean = context.getEntityBackingBean(Employee.class, containerBean);
EntityImportDataSource dataSource = new FileDataSource("HR", employeeXmlFile);
HandleResult result = employeeBean.importEntities(dataSource, ConflictAction.CREATE_NEW, entityRemap);
totalResults.add(result);
}
// expenseClaims
File expenseClaimsXmlFile = new File(tempDir, "expenseClaims.xml");
if (expenseClaimsXmlFile.exists()) {
EntityBackingBean<ExpenseClaim> expenseClaimBean = context.getEntityBackingBean(ExpenseClaim.class, containerBean);
EntityImportDataSource dataSource = new FileDataSource("HR", expenseClaimsXmlFile);
HandleResult result = expenseClaimBean.importEntities(dataSource, ConflictAction.CREATE_NEW, entityRemap);
totalResults.add(result);
}
return totalResults;
} catch (Throwable t) {
throw new BackingBeanException(t);
} finally {
FileUtil.deleteDir(tempDir);
}
}