001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.gl.batch.service.impl;
017    
018    import java.io.File;
019    import java.io.InputStream;
020    import java.util.HashSet;
021    import java.util.List;
022    import java.util.Set;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.kuali.kfs.gl.batch.EnterpriseFeedStep;
026    import org.kuali.kfs.gl.batch.service.EnterpriseFeederNotificationService;
027    import org.kuali.kfs.sys.KFSConstants;
028    import org.kuali.kfs.sys.KFSKeyConstants;
029    import org.kuali.kfs.sys.Message;
030    import org.kuali.rice.kns.mail.MailMessage;
031    import org.kuali.rice.kns.service.KualiConfigurationService;
032    import org.kuali.rice.kns.service.MailService;
033    import org.kuali.rice.kns.service.ParameterService;
034    
035    /**
036     * The base implementation of EnterpriseFeederNotificationService; performs email-based notifications
037     */
038    public class EnterpriseFeederNotificationServiceImpl implements EnterpriseFeederNotificationService {
039        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EnterpriseFeederNotificationServiceImpl.class);
040    
041        private ParameterService parameterService;
042        private KualiConfigurationService configurationService;
043        private MailService mailService;
044    
045        /**
046         * Performs notification about the status of the upload (i.e. feeding) of a single file set (i.e. done file, data file, and
047         * recon file).
048         * 
049         * @param feederProcessName The name of the feeder process; this may correspond to the name of the Spring definition of the
050         *        feeder step, but each implementation may define how to use the value of this parameter and/or restrictions on its
051         *        value.
052         * @param event The event/status of the upload of the file set
053         * @param doneFile The done file
054         * @param dataFile The data file
055         * @param reconFile The recon file
056         * @param errorMessages Any error messages for which to provide notification
057         * @see org.kuali.kfs.gl.batch.service.EnterpriseFeederNotificationService#notifyFileFeedStatus(java.lang.String,
058         *      org.kuali.module.gl.util.EnterpriseFeederEvent, java.io.File, java.io.File, java.io.File, java.util.List)
059         */
060        public void notifyFileFeedStatus(String feederProcessName, EnterpriseFeederStatus status, File doneFile, File dataFile, File reconFile, List<Message> errorMessages) {
061            String doneFileDescription = doneFile == null ? "Done file missing" : doneFile.getAbsolutePath();
062            String dataFileDescription = dataFile == null ? "Data file missing" : dataFile.getAbsolutePath();
063            String reconFileDescription = reconFile == null ? "Recon file missing" : reconFile.getAbsolutePath();
064    
065            // this implementation does not use the contents of the file, so we don't bother opening up the files
066            notifyFileFeedStatus(feederProcessName, status, doneFileDescription, null, dataFileDescription, null, reconFileDescription, null, errorMessages);
067        }
068    
069        /**
070         * Performs notifications
071         * 
072         * @param feederProcessName The name of the feeder process; this may correspond to the name of the Spring definition of the
073         *        feeder step, but each implementation may define how to use the value of this parameter and/or restrictions on its
074         *        value.
075         * @param status The event/status of the upload of the file set
076         * @param doneFileDescription The file name
077         * @param doneFileContents Not used; can be set to null
078         * @param dataFileDescription The file name
079         * @param dataFileContents Not used; can be set to null
080         * @param reconFileDescription The file name
081         * @param reconFileContents Not used; can be set to null
082         * @param errorMessages Any error messages for which to provide notification
083         * @see org.kuali.kfs.gl.batch.service.EnterpriseFeederNotificationService#notifyFileFeedStatus(java.lang.String,
084         *      org.kuali.module.gl.util.EnterpriseFeederEvent, java.lang.String, java.io.InputStream, java.lang.String,
085         *      java.io.InputStream, java.lang.String, java.io.InputStream, java.util.List)
086         */
087        public void notifyFileFeedStatus(String feederProcessName, EnterpriseFeederStatus status, String doneFileDescription, InputStream doneFileContents, String dataFileDescription, InputStream dataFileContents, String reconFileDescription, InputStream reconFileContents, List<Message> errorMessages) {
088            try {
089                if (isStatusNotifiable(feederProcessName, status, doneFileDescription, dataFileDescription, reconFileDescription, errorMessages)) {
090                    Set<String> toEmailAddresses = generateToEmailAddresses(feederProcessName, status, doneFileDescription, dataFileDescription, reconFileDescription, errorMessages);
091                    String fromEmailAddress = mailService.getBatchMailingList();
092    
093                    MailMessage mailMessage = new MailMessage();
094                    mailMessage.setFromAddress(fromEmailAddress);
095                    mailMessage.setToAddresses(toEmailAddresses);
096                    mailMessage.setSubject(getSubjectLine(doneFileDescription, dataFileDescription, reconFileDescription, errorMessages, feederProcessName, status));
097                    mailMessage.setMessage(buildFileFeedStatusMessage(doneFileDescription, dataFileDescription, reconFileDescription, errorMessages, feederProcessName, status));
098    
099                    mailService.sendMessage(mailMessage);
100                }
101            }
102            catch (Exception e) {
103                // Have to try to prevent notification exceptions from breaking control flow in the caller
104                // log and swallow the exception
105                LOG.error("Error occured trying to send notifications.", e);
106            }
107        }
108    
109        /**
110         * Generates the destination address(s) for the email notifications, possibly depending on the parameter values
111         * 
112         * @param feederProcessName The name of the feeder process; this may correspond to the name of the Spring definition of the
113         *        feeder step, but each implementation may define how to use the value of this parameter and/or restrictions on its
114         *        value.
115         * @param status The event/status of the upload of the file set
116         * @param doneFileDescription The file name
117         * @param dataFileDescription The file name
118         * @param reconFileDescription The file name
119         * @param errorMessages Any error messages for which to provide notification
120         * @return the destination addresses
121         */
122        protected Set<String> generateToEmailAddresses(String feederProcessName, EnterpriseFeederStatus status, String doneFileDescription, String dataFileDescription, String reconFileDescription, List<Message> errorMessages) {
123            Set<String> addresses = new HashSet<String>();
124            String[] addressesArray = parameterService.getParameterValues(EnterpriseFeedStep.class, KFSConstants.EnterpriseFeederApplicationParameterKeys.TO_ADDRESS).toArray(new String[] {});
125            for (String address : addressesArray) {
126                addresses.add(address);
127            }
128            return addresses;
129        }
130    
131        /**
132         * Generates the "From:" address for the email
133         * 
134         * @param feederProcessName The name of the feeder process; this may correspond to the name of the Spring definition of the
135         *        feeder step, but each implementation may define how to use the value of this parameter and/or restrictions on its
136         *        value.
137         * @param status The event/status of the upload of the file set
138         * @param doneFileDescription The file name
139         * @param dataFileDescription The file name
140         * @param reconFileDescription The file name
141         * @param errorMessages Any error messages for which to provide notification
142         * @return the source address
143         */
144        protected String generateFromEmailAddress(String feederProcessName, EnterpriseFeederStatus status, String doneFileDescription, String dataFileDescription, String reconFileDescription, List<Message> errorMessages) {
145            return mailService.getBatchMailingList();
146        }
147    
148        /**
149         * Generates the status message that would be generated by a call to notifyFileFeedStatus with the same parameters.
150         * 
151         * @param feederProcessName The name of the feeder process; this may correspond to the name of the Spring definition of the
152         *        feeder step, but each implementation may define how to use the value of this parameter and/or restrictions on its
153         *        value.
154         * @param event The event/status of the upload of the file set
155         * @param doneFile The done file
156         * @param dataFile The data file
157         * @param reconFile The recon file
158         * @param errorMessages Any error messages for which to provide notification
159         * @see org.kuali.kfs.gl.batch.service.EnterpriseFeederNotificationService#getFileFeedStatusMessage(java.lang.String,
160         *      org.kuali.module.gl.util.EnterpriseFeederEvent, java.io.File, java.io.File, java.io.File, java.util.List)
161         */
162        public String getFileFeedStatusMessage(String feederProcessName, EnterpriseFeederStatus status, File doneFile, File dataFile, File reconFile, List<Message> errorMessages) {
163            String doneFileDescription = doneFile.getAbsolutePath();
164            String dataFileDescription = dataFile.getAbsolutePath();
165            String reconFileDescription = reconFile.getAbsolutePath();
166    
167            return buildFileFeedStatusMessage(doneFileDescription, dataFileDescription, reconFileDescription, errorMessages, feederProcessName, status);
168        }
169    
170        /**
171         * @see org.kuali.kfs.gl.batch.service.EnterpriseFeederNotificationService#getFileFeedStatusMessage(java.lang.String,
172         *      org.kuali.module.gl.util.EnterpriseFeederEvent, java.lang.String, java.io.InputStream, java.lang.String,
173         *      java.io.InputStream, java.lang.String, java.io.InputStream, java.util.List)
174         */
175        public String getFileFeedStatusMessage(String feederProcessName, EnterpriseFeederStatus status, String doneFileDescription, InputStream doneFileContents, String dataFileDescription, InputStream dataFileContents, String reconFileDescription, InputStream reconFileContents, List<Message> errorMessages) {
176            return buildFileFeedStatusMessage(doneFileDescription, dataFileDescription, reconFileDescription, errorMessages, feederProcessName, status);
177        }
178    
179        /**
180         * Builds the status message for the status of a feed.
181         * 
182         * @param doneFileDescription the name of the done file
183         * @param dataFileDescription the name of the file to read data from
184         * @param reconFileDescription the name of the reconciliation file
185         * @param errorMessages a List of error messages
186         * @param feederProcessName the name of the feeder process
187         * @return the String of the subject line
188         */
189        protected String getSubjectLine(String doneFileDescription, String dataFileDescription, String reconFileDescription, List<Message> errorMessages, String feederProcessName, EnterpriseFeederStatus status) {
190            String subject = configurationService.getPropertyString(KFSKeyConstants.ERROR_ENTERPRISE_FEEDER_RECONCILIATION_OR_LOADING_ERROR);
191            if (subject == null) {
192                return "ERROR in reconciling or loading GL origin entries from file.";
193            }
194            String productionEnvironmentCode = configurationService.getPropertyString(KFSConstants.PROD_ENVIRONMENT_CODE_KEY);
195            String environmentCode = configurationService.getPropertyString(KFSConstants.ENVIRONMENT_KEY);
196            if (!StringUtils.equals(productionEnvironmentCode, environmentCode)) {
197                subject = environmentCode + ": " + subject;
198            }
199            return subject;
200        }
201    
202        /**
203         * Builds the status message for the status of a feed.
204         * 
205         * @param doneFileName the name of the done file
206         * @param dataFileName the name of the file to get data from
207         * @param reconFileName the reconciliation file
208         * @param errorMessages a List of error messages generated during the process
209         * @param feederProcessName the name of the feeder process
210         * @return a String with the status message
211         */
212        protected String buildFileFeedStatusMessage(String doneFileName, String dataFileName, String reconFileName, List<Message> errorMessages, String feederProcessName, EnterpriseFeederStatus status) {
213            StringBuilder buf = new StringBuilder();
214    
215            buf.append("Data file: ").append(dataFileName).append("\n");
216            buf.append("Reconciliation File: ").append(reconFileName).append("\n");
217            buf.append("Done file: ").append(doneFileName).append("\n\n\n");
218    
219            buf.append("Status: ").append(status.getStatusDescription()).append("\n\n\n");
220    
221            if (status.isErrorEvent()) {
222                buf.append("The done file has been removed and ");
223                if (StringUtils.isNotBlank(feederProcessName)) {
224                    buf.append(feederProcessName);
225                }
226                else {
227                    buf.append("<process name unavailable>");
228                }
229                buf.append(" will continue without processing this set of files (see below).");
230    
231                buf.append("  Please correct and resend the files for the next day's batch.");
232            }
233    
234            buf.append("\n\n");
235    
236            if (!errorMessages.isEmpty()) {
237                buf.append("Error/warning messages:\n");
238                for (Message message : errorMessages) {
239                    if (message.getType() == Message.TYPE_FATAL) {
240                        buf.append("ERROR: ");
241                    }
242                    if (message.getType() == Message.TYPE_WARNING) {
243                        buf.append("WARNING: ");
244                    }
245                    buf.append(message.getMessage()).append("\n");
246                }
247            }
248    
249            return buf.toString();
250        }
251    
252        /**
253         * Returns whether a notification is necessary given the values of the parameters
254         * 
255         * @param feederProcessName the name of the process that invoked the feeder
256         * @param status the status of the feed
257         * @param doneFileDescription the done file description
258         * @param dataFileDescription the data file description
259         * @param reconFileDescription the recon file description
260         * @param errorMessages a list of error messages
261         * @return whether to notify
262         */
263        protected boolean isStatusNotifiable(String feederProcessName, EnterpriseFeederStatus status, String doneFileDescription, String dataFileDescription, String reconFileDescription, List<Message> errorMessages) {
264            if (status instanceof FileReconOkLoadOkStatus) {
265                return false;
266            }
267            return true;
268        }
269    
270    
271        /**
272         * Sets the mailService attribute value.
273         * 
274         * @param mailService The mailService to set.
275         */
276        public void setMailService(MailService mailService) {
277            this.mailService = mailService;
278        }
279    
280        public void setConfigurationService(KualiConfigurationService configurationService) {
281            this.configurationService = configurationService;
282        }
283    
284        public void setParameterService(ParameterService parameterService) {
285            this.parameterService = parameterService;
286        }
287    }