com.gst.infrastructure.reportmailingjob.service.ReportMailingJobWritePlatformServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.gst.infrastructure.reportmailingjob.service.ReportMailingJobWritePlatformServiceImpl.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.gst.infrastructure.reportmailingjob.service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.apache.commons.lang.StringUtils;
import com.gst.infrastructure.core.api.JsonCommand;
import com.gst.infrastructure.core.data.CommandProcessingResult;
import com.gst.infrastructure.core.data.CommandProcessingResultBuilder;
import com.gst.infrastructure.core.exception.PlatformDataIntegrityException;
import com.gst.infrastructure.core.service.DateUtils;
import com.gst.infrastructure.dataqueries.domain.Report;
import com.gst.infrastructure.dataqueries.domain.ReportRepositoryWrapper;
import com.gst.infrastructure.dataqueries.service.ReadReportingService;
import com.gst.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
import com.gst.infrastructure.jobs.annotation.CronTarget;
import com.gst.infrastructure.jobs.exception.JobExecutionException;
import com.gst.infrastructure.jobs.service.JobName;
import com.gst.infrastructure.report.provider.ReportingProcessServiceProvider;
import com.gst.infrastructure.report.service.ReportingProcessService;
import com.gst.infrastructure.reportmailingjob.ReportMailingJobConstants;
import com.gst.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat;
import com.gst.infrastructure.reportmailingjob.data.ReportMailingJobEmailData;
import com.gst.infrastructure.reportmailingjob.data.ReportMailingJobPreviousRunStatus;
import com.gst.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption;
import com.gst.infrastructure.reportmailingjob.domain.ReportMailingJob;
import com.gst.infrastructure.reportmailingjob.domain.ReportMailingJobRepository;
import com.gst.infrastructure.reportmailingjob.domain.ReportMailingJobRepositoryWrapper;
import com.gst.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistory;
import com.gst.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository;
import com.gst.infrastructure.reportmailingjob.util.ReportMailingJobDateUtil;
import com.gst.infrastructure.reportmailingjob.validation.ReportMailingJobValidator;
import com.gst.infrastructure.security.service.PlatformSecurityContext;
import com.gst.portfolio.calendar.service.CalendarUtils;
import com.gst.useradministration.domain.AppUser;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sun.jersey.core.util.MultivaluedMapImpl;

@Service
public class ReportMailingJobWritePlatformServiceImpl implements ReportMailingJobWritePlatformService {

    private final static Logger logger = LoggerFactory.getLogger(ReportMailingJobWritePlatformServiceImpl.class);
    private final ReportRepositoryWrapper reportRepositoryWrapper;
    private final ReportMailingJobValidator reportMailingJobValidator;
    private final ReportMailingJobRepositoryWrapper reportMailingJobRepositoryWrapper;
    private final ReportMailingJobRepository reportMailingJobRepository;
    private final PlatformSecurityContext platformSecurityContext;
    private final ReportMailingJobEmailService reportMailingJobEmailService;
    private final ReadReportingService readReportingService;
    private final ReportingProcessServiceProvider reportingProcessServiceProvider;
    private final ReportMailingJobRunHistoryRepository reportMailingJobRunHistoryRepository;
    private final static String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    @Autowired
    public ReportMailingJobWritePlatformServiceImpl(final ReportRepositoryWrapper reportRepositoryWrapper,
            final ReportMailingJobValidator reportMailingJobValidator,
            final ReportMailingJobRepositoryWrapper reportMailingJobRepositoryWrapper,
            final ReportMailingJobRepository reportMailingJobRepository,
            final PlatformSecurityContext platformSecurityContext,
            final ReportMailingJobEmailService reportMailingJobEmailService,
            final ReadReportingService readReportingService,
            final ReportMailingJobRunHistoryRepository reportMailingJobRunHistoryRepository,
            final ReportingProcessServiceProvider reportingProcessServiceProvider) {
        this.reportRepositoryWrapper = reportRepositoryWrapper;
        this.reportMailingJobValidator = reportMailingJobValidator;
        this.reportMailingJobRepositoryWrapper = reportMailingJobRepositoryWrapper;
        this.reportMailingJobRepository = reportMailingJobRepositoryWrapper.getReportMailingJobRepository();
        this.platformSecurityContext = platformSecurityContext;
        this.reportMailingJobEmailService = reportMailingJobEmailService;
        this.readReportingService = readReportingService;
        this.reportMailingJobRunHistoryRepository = reportMailingJobRunHistoryRepository;
        this.reportingProcessServiceProvider = reportingProcessServiceProvider;
    }

    @Override
    @Transactional
    public CommandProcessingResult createReportMailingJob(JsonCommand jsonCommand) {
        try {
            // validate the create request
            this.reportMailingJobValidator.validateCreateRequest(jsonCommand);

            final AppUser appUser = this.platformSecurityContext.authenticatedUser();

            // get the stretchy Report object
            final Report stretchyReport = this.reportRepositoryWrapper.findOneThrowExceptionIfNotFound(
                    jsonCommand.longValueOfParameterNamed(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME));

            // create an instance of ReportMailingJob class from the JsonCommand object
            final ReportMailingJob reportMailingJob = ReportMailingJob.newInstance(jsonCommand, stretchyReport,
                    appUser);

            // save entity
            this.reportMailingJobRepository.save(reportMailingJob);

            return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId())
                    .withEntityId(reportMailingJob.getId()).build();
        } catch (final DataIntegrityViolationException dve) {
            handleDataIntegrityIssues(jsonCommand, dve);

            return CommandProcessingResult.empty();
        }
    }

    @Override
    @Transactional
    public CommandProcessingResult updateReportMailingJob(Long reportMailingJobId, JsonCommand jsonCommand) {
        try {
            // validate the update request
            this.reportMailingJobValidator.validateUpdateRequest(jsonCommand);

            // retrieve the ReportMailingJob object from the database
            final ReportMailingJob reportMailingJob = this.reportMailingJobRepositoryWrapper
                    .findOneThrowExceptionIfNotFound(reportMailingJobId);

            final Map<String, Object> changes = reportMailingJob.update(jsonCommand);

            // get the recurrence rule string
            final String recurrence = reportMailingJob.getRecurrence();

            // get the next run DateTime from the ReportMailingJob entity
            DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime();

            // check if the stretchy report id was updated
            if (changes.containsKey(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME)) {
                final Long stretchyReportId = (Long) changes
                        .get(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME);
                final Report stretchyReport = this.reportRepositoryWrapper
                        .findOneThrowExceptionIfNotFound(stretchyReportId);

                // update the stretchy report
                reportMailingJob.update(stretchyReport);
            }

            // check if the recurrence was updated
            if (changes.containsKey(ReportMailingJobConstants.RECURRENCE_PARAM_NAME)) {

                // go ahead if the recurrence is not null
                if (StringUtils.isNotBlank(recurrence)) {
                    // set the start DateTime to the current tenant date time
                    DateTime startDateTime = DateUtils.getLocalDateTimeOfTenant().toDateTime();

                    // check if the start DateTime was updated
                    if (changes.containsKey(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME)) {
                        // get the updated start DateTime
                        startDateTime = reportMailingJob.getStartDateTime();
                    }

                    startDateTime = reportMailingJob.getStartDateTime();

                    // get the next recurring DateTime
                    final DateTime nextRecurringDateTime = this.createNextRecurringDateTime(recurrence,
                            startDateTime);

                    // update the next run time property
                    reportMailingJob.updateNextRunDateTime(nextRecurringDateTime);

                    // check if the next run DateTime is not empty and the recurrence is empty
                } else if (StringUtils.isBlank(recurrence) && (nextRunDateTime != null)) {
                    // the next run DateTime should be set to null
                    reportMailingJob.updateNextRunDateTime(null);
                }
            }

            if (changes.containsKey(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME)) {
                final DateTime startDateTime = reportMailingJob.getStartDateTime();

                // initially set the next recurring date time to the new start date time
                DateTime nextRecurringDateTime = startDateTime;

                // ensure that the recurrence pattern string is not empty
                if (StringUtils.isNotBlank(recurrence)) {
                    // get the next recurring DateTime
                    nextRecurringDateTime = this.createNextRecurringDateTime(recurrence, startDateTime);
                }

                // update the next run time property
                reportMailingJob.updateNextRunDateTime(nextRecurringDateTime);
            }

            if (!changes.isEmpty()) {
                // save and flush immediately so any data integrity exception can be handled in the "catch" block
                this.reportMailingJobRepository.saveAndFlush(reportMailingJob);
            }

            return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId())
                    .withEntityId(reportMailingJob.getId()).with(changes).build();
        } catch (final DataIntegrityViolationException dve) {
            handleDataIntegrityIssues(jsonCommand, dve);

            return CommandProcessingResult.empty();
        }
    }

    @Override
    @Transactional
    public CommandProcessingResult deleteReportMailingJob(Long reportMailingJobId) {
        // retrieve the ReportMailingJob object from the database
        final ReportMailingJob reportMailingJob = this.reportMailingJobRepositoryWrapper
                .findOneThrowExceptionIfNotFound(reportMailingJobId);

        // delete the report mailing job by setting the isDeleted property to 1 and altering the name
        reportMailingJob.delete();

        // save the report mailing job entity
        this.reportMailingJobRepository.save(reportMailingJob);

        return new CommandProcessingResultBuilder().withEntityId(reportMailingJobId).build();
    }

    @Override
    @CronTarget(jobName = JobName.EXECUTE_REPORT_MAILING_JOBS)
    public void executeReportMailingJobs() throws JobExecutionException {
        final Collection<ReportMailingJob> reportMailingJobCollection = this.reportMailingJobRepository
                .findByIsActiveTrueAndIsDeletedFalse();

        for (ReportMailingJob reportMailingJob : reportMailingJobCollection) {
            // get the tenant's date as a DateTime object
            final DateTime localDateTimeOftenant = DateUtils.getLocalDateTimeOfTenant().toDateTime();
            final DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime();

            if (nextRunDateTime != null && nextRunDateTime.isBefore(localDateTimeOftenant)) {
                // get the emailAttachmentFileFormat enum object
                final ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat = ReportMailingJobEmailAttachmentFileFormat
                        .newInstance(reportMailingJob.getEmailAttachmentFileFormat());

                if (emailAttachmentFileFormat != null && emailAttachmentFileFormat.isValid()) {
                    final Report stretchyReport = reportMailingJob.getStretchyReport();
                    final String reportName = (stretchyReport != null) ? stretchyReport.getReportName() : null;
                    final StringBuilder errorLog = new StringBuilder();
                    final Map<String, String> validateStretchyReportParamMap = this.reportMailingJobValidator
                            .validateStretchyReportParamMap(reportMailingJob.getStretchyReportParamMap());
                    MultivaluedMap<String, String> reportParams = new MultivaluedMapImpl();

                    if (validateStretchyReportParamMap != null) {
                        Iterator<Map.Entry<String, String>> validateStretchyReportParamMapEntries = validateStretchyReportParamMap
                                .entrySet().iterator();

                        while (validateStretchyReportParamMapEntries.hasNext()) {
                            Map.Entry<String, String> validateStretchyReportParamMapEntry = validateStretchyReportParamMapEntries
                                    .next();
                            String key = validateStretchyReportParamMapEntry.getKey();
                            String value = validateStretchyReportParamMapEntry.getValue();

                            if (StringUtils.containsIgnoreCase(key, "date")) {
                                ReportMailingJobStretchyReportParamDateOption reportMailingJobStretchyReportParamDateOption = ReportMailingJobStretchyReportParamDateOption
                                        .newInstance(value);

                                if (reportMailingJobStretchyReportParamDateOption.isValid()) {
                                    value = ReportMailingJobDateUtil
                                            .getDateAsString(reportMailingJobStretchyReportParamDateOption);
                                }
                            }

                            reportParams.add(key, value);
                        }
                    }

                    // generate the report output stream, method in turn call another that sends the file to the email recipients
                    this.generateReportOutputStream(reportMailingJob, emailAttachmentFileFormat, reportParams,
                            reportName, errorLog);

                    // update the previous run time, next run time, status, error log properties
                    this.updateReportMailingJobAfterJobExecution(reportMailingJob, errorLog, localDateTimeOftenant);
                }
            }
        }
    }

    /** 
     * update the report mailing job entity after job execution 
     * 
     * @param reportMailingJob -- the report mailing job entity
     * @param errorLog -- StringBuilder object containing the error log if any
     * @param jobStartDateTime -- the start DateTime of the job
     * @return None
     **/
    private void updateReportMailingJobAfterJobExecution(final ReportMailingJob reportMailingJob,
            final StringBuilder errorLog, final DateTime jobStartDateTime) {
        final String recurrence = reportMailingJob.getRecurrence();
        final DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime();
        ReportMailingJobPreviousRunStatus reportMailingJobPreviousRunStatus = ReportMailingJobPreviousRunStatus.SUCCESS;

        reportMailingJob.updatePreviousRunErrorLog(null);

        if (errorLog != null && errorLog.length() > 0) {
            reportMailingJobPreviousRunStatus = ReportMailingJobPreviousRunStatus.ERROR;
            reportMailingJob.updatePreviousRunErrorLog(errorLog.toString());
        }

        reportMailingJob.increaseNumberOfRunsByOne();
        reportMailingJob.updatePreviousRunStatus(reportMailingJobPreviousRunStatus.getValue());
        reportMailingJob.updatePreviousRunDateTime(reportMailingJob.getNextRunDateTime());

        // check if the job has a recurrence pattern, if not deactivate the job. The job will only run once
        if (StringUtils.isEmpty(recurrence)) {
            // deactivate job
            reportMailingJob.deactivate();

            // job will only run once, no next run time
            reportMailingJob.updateNextRunDateTime(null);
        } else if (nextRunDateTime != null) {
            final DateTime nextRecurringDateTime = this.createNextRecurringDateTime(recurrence, nextRunDateTime);

            // finally update the next run date time property
            reportMailingJob.updateNextRunDateTime(nextRecurringDateTime);
        }

        // save the ReportMailingJob entity
        this.reportMailingJobRepository.save(reportMailingJob);

        // create a new report mailing job run history entity
        this.createReportMailingJobRunHistroryAfterJobExecution(reportMailingJob, errorLog, jobStartDateTime,
                reportMailingJobPreviousRunStatus.getValue());
    }

    /**
     * create the next recurring DateTime from recurrence pattern, start DateTime and current DateTime
     * 
     * @param recurrencePattern
     * @param startDateTime
     * @return DateTime object
     */
    private DateTime createNextRecurringDateTime(final String recurrencePattern, final DateTime startDateTime) {
        DateTime nextRecurringDateTime = null;

        // the recurrence pattern/rule cannot be empty
        if (StringUtils.isNotBlank(recurrencePattern) && startDateTime != null) {
            final LocalDate nextDayLocalDate = startDateTime.plus(1).toLocalDate();
            final LocalDate nextRecurringLocalDate = CalendarUtils.getNextRecurringDate(recurrencePattern,
                    startDateTime.toLocalDate(), nextDayLocalDate);
            final String nextDateTimeString = nextRecurringLocalDate + " " + startDateTime.getHourOfDay() + ":"
                    + startDateTime.getMinuteOfHour() + ":" + startDateTime.getSecondOfMinute();
            final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(DATETIME_FORMAT);

            nextRecurringDateTime = DateTime.parse(nextDateTimeString, dateTimeFormatter);
        }

        return nextRecurringDateTime;
    }

    /** 
     * create a new report mailing job run history entity after job execution
     * 
     * @param reportMailingJob -- the report mailing job entity
     * @param errorLog -- StringBuilder object containing the error log if any
     * @param jobStartDateTime -- the start DateTime of the job
     * @param jobRunStatus -- the status of the job (success/error)
     * @return None
     **/
    private void createReportMailingJobRunHistroryAfterJobExecution(final ReportMailingJob reportMailingJob,
            final StringBuilder errorLog, final DateTime jobStartDateTime, final String jobRunStatus) {
        final DateTime jobEndDateTime = DateUtils.getLocalDateTimeOfTenant().toDateTime();
        final String errorLogToString = (errorLog != null) ? errorLog.toString() : null;
        final ReportMailingJobRunHistory reportMailingJobRunHistory = ReportMailingJobRunHistory.newInstance(
                reportMailingJob, jobStartDateTime, jobEndDateTime, jobRunStatus, null, errorLogToString);

        this.reportMailingJobRunHistoryRepository.save(reportMailingJobRunHistory);
    }

    /** 
     * Handle any SQL data integrity issue 
     *
     * @param jsonCommand -- JsonCommand object
     * @param dve -- data integrity exception object
     * @return None
     **/
    private void handleDataIntegrityIssues(final JsonCommand jsonCommand,
            final DataIntegrityViolationException dve) {
        final Throwable realCause = dve.getMostSpecificCause();

        if (realCause.getMessage().contains(ReportMailingJobConstants.NAME_PARAM_NAME)) {
            final String name = jsonCommand.stringValueOfParameterNamed(ReportMailingJobConstants.NAME_PARAM_NAME);
            throw new PlatformDataIntegrityException("error.msg.report.mailing.job.duplicate.name",
                    "Report mailing job with name `" + name + "` already exists",
                    ReportMailingJobConstants.NAME_PARAM_NAME, name);
        }

        logger.error(dve.getMessage(), dve);

        throw new PlatformDataIntegrityException("error.msg.charge.unknown.data.integrity.issue",
                "Unknown data integrity issue with resource: " + realCause.getMessage());
    }

    /** 
     * generate the report output stream
     * 
     * @param reportMailingJob
     * @param emailAttachmentFileFormat
     * @param reportParams
     * @param reportName
     * @param errorLog
     * @return the error log StringBuilder object
     */
    private StringBuilder generateReportOutputStream(final ReportMailingJob reportMailingJob,
            final ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat,
            final MultivaluedMap<String, String> reportParams, final String reportName,
            final StringBuilder errorLog) {

        try {
            final String reportType = this.readReportingService.getReportType(reportName);
            final ReportingProcessService reportingProcessService = this.reportingProcessServiceProvider
                    .findReportingProcessService(reportType);

            if (reportingProcessService != null) {
                final Response processReport = reportingProcessService.processRequest(reportName, reportParams);
                final Object reponseObject = (processReport != null) ? processReport.getEntity() : null;

                if (reponseObject != null && reponseObject.getClass().equals(ByteArrayOutputStream.class)) {
                    final ByteArrayOutputStream byteArrayOutputStream = ByteArrayOutputStream.class
                            .cast(reponseObject);
                    final String fileLocation = FileSystemContentRepository.FINERACT_BASE_DIR + File.separator + "";
                    final String fileNameWithoutExtension = fileLocation + File.separator + reportName;

                    // check if file directory exists, if not create directory
                    if (!new File(fileLocation).isDirectory()) {
                        new File(fileLocation).mkdirs();
                    }

                    if ((byteArrayOutputStream == null) || byteArrayOutputStream.size() == 0) {
                        errorLog.append("Report processing failed, empty output stream created");
                    } else if ((errorLog != null && errorLog.length() == 0) && (byteArrayOutputStream.size() > 0)) {
                        final String fileName = fileNameWithoutExtension + "."
                                + emailAttachmentFileFormat.getValue();

                        // send the file to email recipients
                        this.sendReportFileToEmailRecipients(reportMailingJob, fileName, byteArrayOutputStream,
                                errorLog);
                    }
                } else {
                    errorLog.append("Response object entity is not equal to ByteArrayOutputStream ---------- ");
                }
            } else {
                errorLog.append("ReportingProcessService object is null ---------- ");
            }
        } catch (Exception e) {
            errorLog.append(
                    "The ReportMailingJobWritePlatformServiceImpl.generateReportOutputStream method threw an Exception: "
                            + e + " ---------- ");
        }

        return errorLog;
    }

    /** 
     * send report file to email recipients
     * 
     * @param reportMailingJob
     * @param fileName
     * @param byteArrayOutputStream
     * @param errorLog
     */
    private void sendReportFileToEmailRecipients(final ReportMailingJob reportMailingJob, final String fileName,
            final ByteArrayOutputStream byteArrayOutputStream, final StringBuilder errorLog) {
        final Set<String> emailRecipients = this.reportMailingJobValidator
                .validateEmailRecipients(reportMailingJob.getEmailRecipients());

        try {
            final File file = new File(fileName);
            final FileOutputStream outputStream = new FileOutputStream(file);
            byteArrayOutputStream.writeTo(outputStream);

            for (String emailRecipient : emailRecipients) {
                final ReportMailingJobEmailData reportMailingJobEmailData = new ReportMailingJobEmailData(
                        emailRecipient, reportMailingJob.getEmailMessage(), reportMailingJob.getEmailSubject(),
                        file);

                this.reportMailingJobEmailService.sendEmailWithAttachment(reportMailingJobEmailData);
            }

            outputStream.close();

        } catch (IOException e) {
            errorLog.append(
                    "The ReportMailingJobWritePlatformServiceImpl.sendReportFileToEmailRecipients method threw an IOException "
                            + "exception: " + e + " ---------- ");
        }
    }
}