Java tutorial
/* * Kuali Coeus, a comprehensive research administration system for higher education. * * Copyright 2005-2015 Kuali, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.coeus.propdev.impl.s2s.schedule; import gov.grants.apply.services.applicantwebservices_v2.GetApplicationListResponse; import gov.grants.apply.services.applicantwebservices_v2.GetApplicationListResponse.ApplicationInfo; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.kuali.coeus.propdev.impl.core.ProposalDevelopmentDocument; import org.kuali.coeus.propdev.impl.s2s.S2sAppSubmission; import org.kuali.coeus.propdev.impl.s2s.S2sAppSubmissionConstants; import org.kuali.coeus.propdev.impl.s2s.S2sSubmissionService; import org.kuali.coeus.propdev.impl.s2s.connect.S2sCommunicationException; import org.kuali.coeus.propdev.impl.core.DevelopmentProposal; import org.kuali.rice.core.api.mail.MailMessage; import org.kuali.rice.krad.exception.InvalidAddressException; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.MailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import javax.mail.MessagingException; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.*; /** * * This class is run by the S2S Scheduler. ON every trigger, it polls data from Grants.gov for status of submitted proposals. On * receiving status, if it has changed from what exists in database, it updates the status in database and also sends emails * regarding status. All the required parameter configurations are injected from spring-beans.xml * * @author Kuali Research Administration Team (kualidev@oncourse.iu.edu) */ public class S2SPollingTask { private static final Log LOG = LogFactory.getLog(S2SPollingTask.class); private final List<String> lstStatus = new ArrayList<String>(); private final Map<String, String> sortMsgKeyMap = new Hashtable<String, String>(); @Autowired @Qualifier("businessObjectService") private BusinessObjectService businessObjectService = null; @Autowired @Qualifier("s2sSubmissionService") private S2sSubmissionService s2sSubmissionService; @Autowired @Qualifier("mailService") private MailService mailService; private String stopPollInterval; private String mailInterval; private Map<String, String> statusMap = new HashMap<String, String>(); private List<MailInfo> mailInfoList = new ArrayList<MailInfo>(); private static final String KEY_PROPOSAL_NUMBER = "proposalNumber"; private static final String KEY_STATUS = "status"; private static final String DATE_FORMAT = "dd-MMM-yyyy hh:mm a"; private static final String SORT_ID_A = "A"; private static final String SORT_ID_B = "B"; private static final String SORT_ID_C = "C"; private static final String SORT_ID_D = "D"; private static final String SORT_ID_E = "E"; private static final String SORT_ID_F = "F"; private static final String SORT_ID_Z = "Z"; public S2SPollingTask() { lstStatus.add(S2sAppSubmissionConstants.GRANTS_GOV_SUBMISSION_MESSAGE.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_RECEIVING.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_RECEIVED.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_PROCESSING.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_VALIDATED.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_RECEIVED_BY_AGENCY.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_AGENCY_TRACKING_NUMBER_ASSIGNED.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_REJECTED_WITH_ERRORS.toUpperCase()); lstStatus.add(S2sAppSubmissionConstants.STATUS_GRANTS_GOV_SUBMISSION_ERROR.toUpperCase()); sortMsgKeyMap.put("A", "Following proposals DID NOT validate at Grants.Gov"); sortMsgKeyMap.put("B", "Following proposals are at an Unknown status at Grants.Gov"); sortMsgKeyMap.put("C", "Following proposals will be dropped from the notification emails in next 24 hours"); sortMsgKeyMap.put("D", "Following submissions status has not changed"); sortMsgKeyMap.put("E", "Following submissions status has changed"); sortMsgKeyMap.put("F", "Error occured while retrieving submissions"); sortMsgKeyMap.put("Z", ""); } /** * * This method determines whether the particular submission record received as parameter must be polled or not based on its last * modified date. * * @param appSubmission * @return boolean */ private boolean getSubmissionDateValidity(S2sAppSubmission appSubmission) { Calendar lastModifiedDate = Calendar.getInstance(); long stopPollingIntervalMillis = Integer.parseInt(this.getStopPollInterval()) * 60 * 60 * 1000L; if (appSubmission.getLastModifiedDate() != null) { lastModifiedDate.setTimeInMillis(appSubmission.getLastModifiedDate().getTime()); } else { lastModifiedDate.setTimeInMillis(appSubmission.getReceivedDate().getTime()); } return (new Date().getTime() - lastModifiedDate.getTimeInMillis() < stopPollingIntervalMillis); } /** * * This method filters out the latest submission record for each proposal and returns it in a map. * * @return map of one submission record for each proposal */ private Map<String, SubmissionData> populatePollingList() { Map<String, String> submissionMap = new HashMap<String, String>(); Collection<S2sAppSubmission> submissionList = new ArrayList<S2sAppSubmission>(); for (String status : statusMap.values()) { submissionMap.clear(); submissionMap.put(KEY_STATUS, status); if (submissionList == null) { submissionList = businessObjectService.findMatching(S2sAppSubmission.class, submissionMap); } else { submissionList.addAll(businessObjectService.findMatching(S2sAppSubmission.class, submissionMap)); } } Map<String, SubmissionData> pollingList = new HashMap<String, SubmissionData>(); Iterator<S2sAppSubmission> appSubmissions = submissionList.iterator(); while (appSubmissions.hasNext()) { S2sAppSubmission appSubmission = appSubmissions.next(); if (pollingList.get(appSubmission.getProposalNumber()) != null) { if (appSubmission.getSubmissionNumber() > pollingList.get(appSubmission.getProposalNumber()) .getS2sAppSubmission().getSubmissionNumber()) { // Greater the submission number, more the latest // Check if the record is not more than 6 months old if (getSubmissionDateValidity(appSubmission)) { SubmissionData submissionData = new SubmissionData(); submissionData.setS2sAppSubmission(appSubmission); pollingList.put(appSubmission.getProposalNumber(), submissionData); } } } else { if (getSubmissionDateValidity(appSubmission)) { SubmissionData submissionData = new SubmissionData(); submissionData.setS2sAppSubmission(appSubmission); pollingList.put(appSubmission.getProposalNumber(), submissionData); } } } return pollingList; } /** * This method is the starting point of execution for the thread that is scheduled by the scheduler service * */ public void execute() { LOG.info("Executing polling schedule for status -" + statusMap.values() + ":" + stopPollInterval); Map<String, SubmissionData> pollingList = populatePollingList(); int appListSize = pollingList.size(); Iterator<SubmissionData> submissions = pollingList.values().iterator(); HashMap<String, Vector<SubmissionData>> htMails = new LinkedHashMap<String, Vector<SubmissionData>>(); Vector<SubmissionData> submList = new Vector<SubmissionData>(); Timestamp[] lastNotiDateArr = new Timestamp[appListSize]; while (submissions.hasNext()) { SubmissionData localSubInfo = submissions.next(); S2sAppSubmission appSubmission = localSubInfo.getS2sAppSubmission(); Timestamp oldLastNotiDate = appSubmission.getLastNotifiedDate(); Timestamp today = new Timestamp(new Date().getTime()); boolean updateFlag = false; boolean sendEmailFlag = false; boolean statusChanged = false; GetApplicationListResponse applicationListResponse = null; try { ProposalDevelopmentDocument pdDoc = getProposalDevelopmentDocument( appSubmission.getProposalNumber()); if (pdDoc != null) { applicationListResponse = s2sSubmissionService.fetchApplicationListResponse(pdDoc); } if (applicationListResponse.getApplicationInfo() == null || applicationListResponse.getApplicationInfo().size() == 0) { statusChanged = s2sSubmissionService.checkForSubmissionStatusChange(pdDoc, appSubmission); if (statusChanged == false && appSubmission.getComments() .equals(S2sAppSubmissionConstants.STATUS_NO_RESPONSE_FROM_GRANTS_GOV)) { localSubInfo.setSortId(SORT_ID_F); sendEmailFlag = true; } } else { ApplicationInfo ggApplication = applicationListResponse.getApplicationInfo().get(0); if (ggApplication != null) { localSubInfo.setAcType('U'); statusChanged = !appSubmission.getStatus() .equalsIgnoreCase(ggApplication.getGrantsGovApplicationStatus().value()); s2sSubmissionService.populateAppSubmission(pdDoc, appSubmission, ggApplication); } } } catch (S2sCommunicationException e) { LOG.error(e.getMessage(), e); appSubmission.setComments(e.getMessage()); localSubInfo.setSortId(SORT_ID_F); sendEmailFlag = true; } String sortId = SORT_ID_Z; Timestamp lastNotifiedDate = appSubmission.getLastNotifiedDate(); Timestamp statusChangedDate = appSubmission.getLastModifiedDate(); Calendar lastNotifiedDateCal = Calendar.getInstance(); if (lastNotifiedDate != null) { lastNotifiedDateCal.setTimeInMillis(lastNotifiedDate.getTime()); } Calendar statusChangedDateCal = Calendar.getInstance(); if (statusChangedDate != null) { statusChangedDateCal.setTimeInMillis(statusChangedDate.getTime()); } Calendar recDateCal = Calendar.getInstance(); recDateCal.setTimeInMillis(appSubmission.getReceivedDate().getTime()); if (statusChanged) { if (appSubmission.getStatus() .indexOf(S2sAppSubmissionConstants.STATUS_GRANTS_GOV_SUBMISSION_ERROR) != -1) { updateFlag = true; sendEmailFlag = true; sortId = SORT_ID_A; } else if (!lstStatus.contains(appSubmission.getStatus().trim().toUpperCase())) { updateFlag = false; sendEmailFlag = true; sortId = SORT_ID_B; } else { updateFlag = true; sendEmailFlag = true; sortId = SORT_ID_E; } } else { long lastModifiedTime = statusChangedDate == null ? appSubmission.getReceivedDate().getTime() : statusChangedDate.getTime(); long lastNotifiedTime = lastNotifiedDate == null ? lastModifiedTime : lastNotifiedDate.getTime(); long mailDelta = today.getTime() - lastNotifiedTime; long delta = today.getTime() - lastModifiedTime; long stopPollDiff = ((Integer.parseInt(getStopPollInterval()) == 0 ? 4320L : Integer.parseInt(getStopPollInterval())) - (delta / (60 * 60 * 1000))); if ((mailDelta / (1000 * 60)) >= (Integer.parseInt(getMailInterval()))) { if (localSubInfo.getSortId() == null) { if (stopPollDiff <= 24) { sortId = SORT_ID_C; } else { sortId = SORT_ID_D; sortMsgKeyMap.put(SORT_ID_D, "Following submissions status has not been changed in " + getMailInterval() + " minutes"); } } updateFlag = true; sendEmailFlag = true; } } if (sendEmailFlag) { Map<String, String> proposalMap = new HashMap<String, String>(); proposalMap.put(KEY_PROPOSAL_NUMBER, appSubmission.getProposalNumber()); DevelopmentProposal developmentProposal = (DevelopmentProposal) businessObjectService .findByPrimaryKey(DevelopmentProposal.class, proposalMap); String dunsNum; if (developmentProposal.getApplicantOrganization().getOrganization().getDunsNumber() != null) { dunsNum = developmentProposal.getApplicantOrganization().getOrganization().getDunsNumber(); } else { dunsNum = developmentProposal.getApplicantOrganization().getOrganizationId(); } Vector<SubmissionData> mailGrpForDunNum = new Vector<SubmissionData>(); mailGrpForDunNum.add(localSubInfo); htMails.put(dunsNum, mailGrpForDunNum); appSubmission.setLastNotifiedDate(today); } if (localSubInfo.getSortId() == null) { localSubInfo.setSortId(sortId); } if (updateFlag) { submList.addElement(localSubInfo); lastNotiDateArr[submList.size() - 1] = oldLastNotiDate; } } try { sendMail(htMails); } catch (InvalidAddressException ex) { LOG.error("Mail sending failed"); LOG.error(ex.getMessage(), ex); int size = submList.size(); for (int i = 0; i < size; i++) { SubmissionData localSubInfo = submList.elementAt(i); localSubInfo.getS2sAppSubmission().setLastNotifiedDate(lastNotiDateArr[i]); } } catch (MessagingException me) { LOG.error("Mail sending failed"); LOG.error(me.getMessage(), me); int size = submList.size(); for (int i = 0; i < size; i++) { SubmissionData localSubInfo = submList.elementAt(i); localSubInfo.getS2sAppSubmission().setLastNotifiedDate(lastNotiDateArr[i]); } } saveSubmissionDetails(submList); } private ProposalDevelopmentDocument getProposalDevelopmentDocument(String proposalNumber) { ProposalDevelopmentDocument pdDoc = null; Map<String, String> proposalMap = new HashMap<String, String>(); proposalMap.put(KEY_PROPOSAL_NUMBER, proposalNumber); DevelopmentProposal developmentProposal = (DevelopmentProposal) businessObjectService .findByPrimaryKey(DevelopmentProposal.class, proposalMap); if (developmentProposal != null) { pdDoc = developmentProposal.getProposalDocument(); } return pdDoc; } /** * * This method saves submission data and status to database * * @param submList */ private void saveSubmissionDetails(Vector<SubmissionData> submList) { if (submList != null) { for (SubmissionData submissionData : submList) { S2sAppSubmission s2sAppSubmission = submissionData.getS2sAppSubmission(); s2sAppSubmission.setUpdateUserSet(true); if (!s2sAppSubmission.getStatus().equalsIgnoreCase(S2sAppSubmissionConstants.STATUS_PUREGED)) businessObjectService.save(s2sAppSubmission); } } } /** * * This method sends mail for all submission status records that have changed relative to database * * @param htMails * @throws InvalidAddressException * @throws Exception */ private void sendMail(HashMap<String, Vector<SubmissionData>> htMails) throws InvalidAddressException, MessagingException { if (htMails.isEmpty()) { return; } List<MailInfo> mailList = getMailInfoList(); for (MailInfo mailInfo : mailList) { String dunsNum = mailInfo.getDunsNumber(); Vector<SubmissionData> propList = htMails.get(dunsNum); if (propList == null) { continue; } htMails.remove(dunsNum); MailMessage mailMessage = parseNGetMailAttr(propList, mailInfo); if (mailMessage != null) { mailService.sendMessage(mailMessage); } } if (mailList.size() > 0 && !htMails.isEmpty()) { Iterator<String> it = htMails.keySet().iterator(); while (it.hasNext()) { Vector<SubmissionData> nonDunsPropList = htMails.get(it.next()); MailInfo mailInfo = mailList.get(0); MailMessage mailMessage = parseNGetMailAttr(nonDunsPropList, mailInfo); if (mailMessage != null) { mailService.sendMessage(mailMessage); LOG.debug("Sent mail with default duns to " + mailMessage.getToAddresses() + " Subject as " + mailMessage.getSubject() + " Message as " + mailMessage.getMessage()); } } } } /** * * This method processes data that is to be sent by mail * * @param propList * @param mailInfo * @return {@link MailMessage} */ private MailMessage parseNGetMailAttr(Vector<SubmissionData> propList, MailInfo mailInfo) { if (propList == null || propList.isEmpty()) { return null; } MailMessage mailMessage = mailInfo.getMailMessage(); StringBuffer message = new StringBuffer(mailMessage.getMessage()); for (SubmissionData submissionData : propList) { S2sAppSubmission appSubmission = submissionData.getS2sAppSubmission(); Timestamp lastNotifiedDate = appSubmission.getLastNotifiedDate(); Timestamp statusChangedDate = appSubmission.getLastModifiedDate(); Calendar lastNotifiedDateCal = Calendar.getInstance(); if (lastNotifiedDate != null) { lastNotifiedDateCal.setTimeInMillis(lastNotifiedDate.getTime()); } Calendar statusChangedDateCal = Calendar.getInstance(); if (statusChangedDate != null) { statusChangedDateCal.setTimeInMillis(statusChangedDate.getTime()); } Calendar recDateCal = Calendar.getInstance(); recDateCal.setTimeInMillis(appSubmission.getReceivedDate().getTime()); long lastModifiedTime = statusChangedDate == null ? appSubmission.getReceivedDate().getTime() : statusChangedDate.getTime(); Timestamp today = new Timestamp(new Date().getTime()); long delta = today.getTime() - lastModifiedTime; double deltaHrs = ((double) Math.round((delta / (1000.0d * 60.0d * 60.0d)) * Math.pow(10.0, 2))) / 100; int days = 0; int hrs = 0; if (deltaHrs > 0) { days = (int) deltaHrs / 24; hrs = (int) (((double) Math.round((deltaHrs % 24) * Math.pow(10.0, 2))) / 100); } if (propList.size() > 0) { SubmissionData prevSubmissionData = propList.elementAt(propList.size() - 1); if (!prevSubmissionData.getSortId().equals(submissionData.getSortId())) { message.append("\n\n"); message.append(sortMsgKeyMap.get(submissionData.getSortId())); message.append("\n____________________________________________________"); } } else { message.append("\n\n"); message.append(sortMsgKeyMap.get(submissionData.getSortId())); message.append("\n____________________________________________________"); } SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); message.append('\n'); message.append("Proposal Number : " + appSubmission.getProposalNumber() + "\n"); message.append("Received Date : "); message.append(dateFormat.format(appSubmission.getReceivedDate())); message.append('\n'); message.append("Grants.Gov Tracking Id : "); message.append(appSubmission.getGgTrackingId()); message.append('\n'); String agTrackId = appSubmission.getAgencyTrackingId() == null ? "Not updated yet" : appSubmission.getAgencyTrackingId(); message.append("Agency Tracking Id : "); message.append(agTrackId); message.append('\n'); message.append("Current Status : "); message.append(appSubmission.getStatus()); message.append('\n'); String stChnageDate = appSubmission.getLastModifiedDate() == null ? "Not updated yet" : dateFormat.format(appSubmission.getLastModifiedDate()); message.append("Last Status Change : " + stChnageDate + "\t *** " + days + " day(s) and " + hrs + " hour(s) ***\n"); message.append('\n'); } message.append('\n'); message.append(mailInfo.getFooter()); mailMessage.setMessage(message.toString()); return mailMessage; } /** * Getter for property stopPollInterval. * * @return Value of property stopPollInterval. */ public String getStopPollInterval() { return stopPollInterval; } /** * Setter for property stopPollInterval. * * @param stopPollInterval New value of property stopPollInterval. */ public void setStopPollInterval(String stopPollInterval) { this.stopPollInterval = stopPollInterval; } /** * Gets the statusMap attribute. * * @return Returns the statusMap. */ public Map<String, String> getStatusMap() { return statusMap; } /** * Sets the statusMap attribute value. * * @param statusMap The statusMap to set. */ public void setStatusMap(Map<String, String> statusMap) { this.statusMap = statusMap; } /** * Gets the mailInfoList attribute. * * @return Returns the mailInfoList. */ public List<MailInfo> getMailInfoList() { return mailInfoList; } /** * Sets the mailInfoList attribute value. * * @param mailInfoList The mailInfoList to set. */ public void setMailInfoList(List<MailInfo> mailInfoList) { this.mailInfoList = mailInfoList; } /** * Gets the mailInterval attribute. * * @return Returns the mailInterval. */ public String getMailInterval() { return mailInterval; } /** * Sets the mailInterval attribute value. * * @param mailInterval The mailInterval to set. */ public void setMailInterval(String mailInterval) { this.mailInterval = mailInterval; } /** * Sets the businessObjectService attribute value. * * @param businessObjectService The businessObjectService to set. */ public void setBusinessObjectService(BusinessObjectService businessObjectService) { this.businessObjectService = businessObjectService; } public BusinessObjectService getBusinessObjectService() { return businessObjectService; } public S2sSubmissionService getS2sSubmissionService() { return s2sSubmissionService; } public void setS2sSubmissionService(S2sSubmissionService s2sSubmissionService) { this.s2sSubmissionService = s2sSubmissionService; } public MailService getMailService() { return mailService; } public void setMailService(MailService mailService) { this.mailService = mailService; } }