Documentation Home

Adding exports to a new domain

  1. Add the export module as a dependency in your POM
  2. Create a new class extending ExportEntityType and add a new type in accordance to the domain that you're adding exports to
  3. Create an admin extension handler to add a button for exporting on the page that you want exporting to be done in the admin

    • Most likely this would be done by creating a class that extends AbstractAdminAbstractControllerExtensionHandler and implements addAdditionalMainActions(). Be sure that one of the button classes is export-standard-action in order for the JavaScript in the export module to automatically setup the onclick listener for it. Example:

      @Override
      public ExtensionResultStatusType addAdditionalMainActions(String sectionClassName, List<EntityFormAction> actions) {
      
          if (sectionClassName.equals(CustomerSegment.class.getName()) || sectionClassName.equals(CustomerSegmentImpl.class.getName())) {
              actions.add(new EntityFormAction("export-customer-segment")
                      .withDisplayText("export_customer_segments")
                      .withIconClass("icon-upload")
                      .withButtonClass("export-standard-action")
                      .withUrlOverride("/export/customer-segment")
              );
              return ExtensionResultStatusType.HANDLED_CONTINUE;
          }
      
          return ExtensionResultStatusType.NOT_HANDLED;
      }
      
  4. Create a controller class extending AdminAbstractExportController

    • Override the viewConfigurePrompt and uploadExport methods with the url you chose for the .withUrlOverride property.
      In order to use the default modal popup provided in the export module be sure that the GET returns super.viewConfigurePrompt(request, response, model), which will return the default modal view "views/configureExportPrompt".
      An example of the GET and POST overriding methods for the CustomerSegment export is shown below

       /**
       * Shows the form that the allows the user to create an export file
       */
      @GetMapping(value = "/customer-segment")
      public String viewConfigurePrompt(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
          return super.viewConfigurePrompt(request, response, model);
      }
      
      /**
       * Used by the Export Prompt to schedule a job so that the export can be processed later.
       */
      @PostMapping(value = "/customer-segment", produces = MediaType.APPLICATION_JSON_VALUE)
      public @ResponseBody Map<String, String> uploadExport(HttpServletRequest request,
                                                            HttpServletResponse response,
                                                            @ModelAttribute(name = "exportContext") ExportContext exportContext,
                                                            BindingResult result) {
          return super.uploadExport(request, response, exportContext, result);
      }
      
    • Override the getDefaultFriendlyName() method and return a default name you want to use for the exported file if the user didn't enter any file friendly name.

       @Override
       protected String getDefaultFriendlyName() {
           return "My Customer Segment Export";
       }
      
    • Override the getExportScheduler() method and return your ExportScheduler that will be defined in the next step.

       @Override
       protected ExportScheduler getExportScheduler() {
           return customerSegmentExportScheduler;
       }
      
    • Override the canUseDateRange() method and return true if you want to allow users to specify a date range for the export.

      @Override
      protected boolean canUseDateRange() {
          return true;
      }
      
  5. Next create an export event scheduler that extends AbstractExportScheduler, and override the getTotalRecords() and getProcessType() methods. The rest will be handled by the AbstractExportScheduler. For example:

    @Component
    @ConditionalOnExport
    public class CustomerSegmentExportScheduler extends AbstractExportScheduler {
    
        @Resource
        protected CustomerSegmentService customerSegmentService;
    
        @Override
        protected Long getTotalRecords() {
            return customerSegmentService.getTotalRecordsCount();
        }
    
        @Override
        protected String getProcessType() {
            return CustomerSegmentExportProcessExecutor.PROCESS_TYPE;
        }
    }
    
  6. Then create an ExportProcessExector that extends AbstractExportProcessExecutor. Override the following methods so that the AbstractExportProcessExecutor can properly handle the export job. For example:

    @ConditionalOnExport
    @Component("blCustomerSegmentExportProcessExecutor")
    public class CustomerSegmentExportProcessExecutor extends AbstractExportProcessExecutor {
    
        public static final String DEFAULT_EXPORT_FILENAME = "customerSegmentExport";
        public static final String PROCESS_TYPE = "CUSTOMER_SEG_EXPORT";
        public static final String ENTITY_TYPE = "CUSTOMER";
        public static final String CUSTOMER_SEGMENT_EXPORT_BATCH_SIZE_PROPERTY = "customerSegments.export.batchSize";
    
        @Resource(name = "blCustomerSegmentExportService")
        protected CustomerSegmentExportService customerSegmentExportService;
    
        @Override
        protected ExportService<?> getExportService() {
            return customerSegmentExportService;
        }
    
        @Override
        protected String getBatchSizePropertyName() {
            return CUSTOMER_SEGMENT_EXPORT_BATCH_SIZE_PROPERTY;
        }
    
        @Override
        protected String getDefaultExportFileName() {
            return DEFAULT_EXPORT_FILENAME;
        }
    
        @Override
        protected String getEntityType() {
            return ENTITY_TYPE;
        }
    
        @Override
        public String getProcessType() {
            return PROCESS_TYPE;
        }
    }
    
  7. Lastly, create a service to actually do the work of exporting. The Executor will call YourEventConsumer.export() method,
    which is implemented in AbstractExportService. Extend AbstractExportService<T, D> and override the necessary methods for a
    clean and easy implementation. For example:

        @ConditionalOnExport
        @Service("blCustomerSegmentExportService")
        public class CustomerSegmentExportServiceImpl extends AbstractExportService<CustomerCustomerSegmentXref, CustomerSegmentDTO> implements CustomerSegmentExportService {
    
            @Resource(name = "blCustomerSegmentService")
            protected CustomerSegmentService customerSegmentService;
    
            @Override
            public List<CustomerCustomerSegmentXref> readBatch(int recordStartingPoint, ExportProcessExecutorContext context) {
                return customerSegmentService.findBatchCustomerCustomerSegXrefs(recordStartingPoint, context.getBatchSize());
            }
    
            @Override
            protected CustomerSegmentDTO wrap(CustomerCustomerSegmentXref customerSegment, ExportProcessExecutorContext exportProcessContext) {
                CustomerSegmentDTO dto = applicationContext.getBean(CustomerSegmentDTO.class);
                return dto.wrap(customerSegment);
            }
    
            @Override
            protected Class<? extends CustomerSegmentDTO> getDtoClass() {
                return CustomerSegmentDTO.class;
            }
    
            @Override
            public Long getTotalRecordsCount() {
                return customerSegmentService.readNumberOfCustomerSegments();
            }
        }
    

    }

If you would wish for your export to support additional formats or encodings then it's advised to create classes extending SupportedExportEncoding and/or SupportedExportType.