View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.pdp.service.impl;
17  
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.PrintStream;
24  import java.sql.Timestamp;
25  import java.text.MessageFormat;
26  import java.util.ArrayList;
27  import java.util.Calendar;
28  import java.util.List;
29  
30  import org.apache.commons.io.IOUtils;
31  import org.apache.commons.lang.StringUtils;
32  import org.kuali.ole.pdp.businessobject.Batch;
33  import org.kuali.ole.pdp.businessobject.CustomerProfile;
34  import org.kuali.ole.pdp.businessobject.LoadPaymentStatus;
35  import org.kuali.ole.pdp.businessobject.PaymentFileLoad;
36  import org.kuali.ole.pdp.businessobject.PaymentGroup;
37  import org.kuali.ole.pdp.service.CustomerProfileService;
38  import org.kuali.ole.pdp.service.PaymentFileService;
39  import org.kuali.ole.pdp.service.PaymentFileValidationService;
40  import org.kuali.ole.pdp.service.PdpEmailService;
41  import org.kuali.ole.sys.OLEConstants;
42  import org.kuali.ole.sys.OLEKeyConstants;
43  import org.kuali.ole.sys.batch.BatchInputFileType;
44  import org.kuali.ole.sys.batch.InitiateDirectoryBase;
45  import org.kuali.ole.sys.batch.service.BatchInputFileService;
46  import org.kuali.ole.sys.exception.ParseException;
47  import org.kuali.rice.core.api.config.property.ConfigurationService;
48  import org.kuali.rice.core.api.datetime.DateTimeService;
49  import org.kuali.rice.core.api.util.type.KualiInteger;
50  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
51  import org.kuali.rice.krad.service.BusinessObjectService;
52  import org.kuali.rice.krad.util.ErrorMessage;
53  import org.kuali.rice.krad.util.GlobalVariables;
54  import org.kuali.rice.krad.util.MessageMap;
55  import org.springframework.transaction.annotation.Transactional;
56  
57  /**
58   * @see org.kuali.ole.pdp.service.PaymentFileService
59   */
60  @Transactional
61  public class PaymentFileServiceImpl extends InitiateDirectoryBase implements PaymentFileService {
62      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentFileServiceImpl.class);
63  
64      private String outgoingDirectoryName;
65  
66      private ParameterService parameterService;
67      private CustomerProfileService customerProfileService;
68      private BatchInputFileService batchInputFileService;
69      private PaymentFileValidationService paymentFileValidationService;
70      private BusinessObjectService businessObjectService;
71      private DateTimeService dateTimeService;
72      private PdpEmailService paymentFileEmailService;
73      private ConfigurationService kualiConfigurationService;
74  
75      public PaymentFileServiceImpl() {
76          super();
77      }
78  
79      /**
80       * @see org.kuali.ole.pdp.service.PaymentFileService#processPaymentFiles(org.kuali.ole.sys.batch.BatchInputFileType)
81       */
82      public void processPaymentFiles(BatchInputFileType paymentInputFileType) {
83          List<String> fileNamesToLoad = batchInputFileService.listInputFileNamesWithDoneFile(paymentInputFileType);
84  
85          for (String incomingFileName : fileNamesToLoad) {
86              try {
87                  if (LOG.isDebugEnabled()) {
88                      LOG.debug("processPaymentFiles() Processing " + incomingFileName);
89                  }
90  
91                  // collect various information for status of load
92                  LoadPaymentStatus status = new LoadPaymentStatus();
93                  status.setMessageMap(new MessageMap());
94  
95                  // process payment file
96                  PaymentFileLoad paymentFile = processPaymentFile(paymentInputFileType, incomingFileName, status.getMessageMap());
97                  if (paymentFile != null && paymentFile.isPassedValidation()) {
98                      // load payment data
99                      loadPayments(paymentFile, status, incomingFileName);
100 
101                 createOutputFile(status, incomingFileName);
102                 }else{
103                     //if we encounter an error for the payment file, we will remove the .done file so it will not be parse again
104                     
105                     LOG.warn("Encounter a problem while processing payment file: " + incomingFileName + " .  Removing the done file to stop re-process.");
106                     removeDoneFile(incomingFileName);
107                 }
108             }
109             catch (RuntimeException e) {
110                 LOG.error("Caught exception trying to load payment file: " + incomingFileName, e);
111                 // swallow exception so we can continue processing files, the errors have been reported by email
112             }
113         }
114     }
115 
116     /**
117      * Attempt to parse the file, run validations, and store batch data
118      * 
119      * @param paymentInputFileType <code>BatchInputFileType</code> for payment files
120      * @param incomingFileName name of payment file
121      * @param errorMap <code>Map</code> of errors
122      * @return <code>LoadPaymentStatus</code> containing status data for load
123      */
124     protected PaymentFileLoad processPaymentFile(BatchInputFileType paymentInputFileType, String incomingFileName, MessageMap errorMap) {
125         // parse xml, if errors found return with failure
126         PaymentFileLoad paymentFile = parsePaymentFile(paymentInputFileType, incomingFileName, errorMap);
127 
128         if (errorMap.hasNoErrors()) {
129             // do validation
130             doPaymentFileValidation(paymentFile, errorMap);
131         }
132 
133         return paymentFile;
134     }
135 
136     /**
137      * @see org.kuali.ole.pdp.service.PaymentFileService#doPaymentFileValidation(org.kuali.ole.pdp.businessobject.PaymentFileLoad,
138      *      org.kuali.rice.krad.util.MessageMap)
139      */
140     public void doPaymentFileValidation(PaymentFileLoad paymentFile, MessageMap errorMap) {
141         paymentFileValidationService.doHardEdits(paymentFile, errorMap);
142 
143         if (errorMap.hasErrors()) {
144             paymentFileEmailService.sendErrorEmail(paymentFile, errorMap);
145         }
146 
147         paymentFile.setPassedValidation(true);
148     }
149 
150     /**
151      * @see org.kuali.ole.pdp.service.PaymentFileService#loadPayments(java.lang.String)
152      */
153     public void loadPayments(PaymentFileLoad paymentFile, LoadPaymentStatus status, String incomingFileName) {
154         status.setChart(paymentFile.getChart());
155         status.setUnit(paymentFile.getUnit());
156         status.setSubUnit(paymentFile.getSubUnit());
157         status.setCreationDate(paymentFile.getCreationDate());
158         status.setDetailCount(paymentFile.getActualPaymentCount());
159         status.setDetailTotal(paymentFile.getCalculatedPaymentTotalAmount());
160 
161         // create batch record for payment load
162         Batch batch = createNewBatch(paymentFile, getBaseFileName(incomingFileName));
163         businessObjectService.save(batch);
164 
165         paymentFile.setBatchId(batch.getId());
166         status.setBatchId(batch.getId());
167 
168         // do warnings and set defaults
169         List<String> warnings = paymentFileValidationService.doSoftEdits(paymentFile);
170         status.setWarnings(warnings);
171 
172         // store groups
173         for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) {
174             businessObjectService.save(paymentGroup);
175         }
176 
177         // send list of warnings
178         paymentFileEmailService.sendLoadEmail(paymentFile, warnings);
179         if (paymentFile.isTaxEmailRequired()) {
180             paymentFileEmailService.sendTaxEmail(paymentFile);
181         }
182 
183         removeDoneFile(incomingFileName);
184 
185         LOG.debug("loadPayments() was successful");
186         status.setLoadStatus(LoadPaymentStatus.LoadStatus.SUCCESS);
187     }
188 
189     /**
190      * Calls <code>BatchInputFileService</code> to validate XML against schema and parse.
191      * 
192      * @param paymentInputFileType <code>BatchInputFileType</code> for payment files
193      * @param incomingFileName name of the payment file to parse
194      * @param errorMap any errors encountered while parsing are adding to
195      * @return <code>PaymentFile</code> containing the parsed values
196      */
197     protected PaymentFileLoad parsePaymentFile(BatchInputFileType paymentInputFileType, String incomingFileName, MessageMap errorMap) {
198         FileInputStream fileContents;
199         try {
200             fileContents = new FileInputStream(incomingFileName);
201         }
202         catch (FileNotFoundException e1) {
203             LOG.error("file to load not found " + incomingFileName, e1);
204             throw new RuntimeException("Cannot find the file requested to be loaded " + incomingFileName, e1);
205         }
206 
207         // do the parse
208         PaymentFileLoad paymentFile = null;
209         try {
210             byte[] fileByteContent = IOUtils.toByteArray(fileContents);
211             paymentFile = (PaymentFileLoad) batchInputFileService.parse(paymentInputFileType, fileByteContent);
212         }
213         catch (IOException e) {
214             LOG.error("error while getting file bytes:  " + e.getMessage(), e);
215             throw new RuntimeException("Error encountered while attempting to get file bytes: " + e.getMessage(), e);
216         }
217         catch (ParseException e1) {
218             LOG.error("Error parsing xml " + e1.getMessage());
219 
220             errorMap.putError(OLEConstants.GLOBAL_ERRORS, OLEKeyConstants.ERROR_BATCH_UPLOAD_PARSING_XML, new String[] { e1.getMessage() });
221 
222             // Send error email
223             paymentFileEmailService.sendErrorEmail(paymentFile, errorMap);
224         }
225 
226         return paymentFile;
227     }
228 
229     /**
230      * @see org.kuali.ole.pdp.service.PaymentFileService#createOutputFile(org.kuali.ole.pdp.businessobject.LoadPaymentStatus,
231      *      java.lang.String)
232      */
233     public boolean createOutputFile(LoadPaymentStatus status, String inputFileName) {
234         
235         //add a step to check for directory paths
236         prepareDirectories(getRequiredDirectoryNames());
237         
238         // construct the outgoing file name
239         String filename = outgoingDirectoryName + "/" + getBaseFileName(inputFileName);
240 
241         // set code-message indicating overall load status
242         String code;
243         String message;
244         if (LoadPaymentStatus.LoadStatus.SUCCESS.equals(status.getLoadStatus())) {
245             code = "SUCCESS";
246             message = "Successful Load";
247         }
248         else {
249             code = "FAIL";
250             message = "Load Failed: ";
251             List<ErrorMessage> errorMessages = status.getMessageMap().getMessages(OLEConstants.GLOBAL_ERRORS);
252             for (ErrorMessage errorMessage : errorMessages) {
253                 String resourceMessage = kualiConfigurationService.getPropertyValueAsString(errorMessage.getErrorKey());
254                 resourceMessage = MessageFormat.format(resourceMessage, (Object[]) errorMessage.getMessageParameters());
255                 message += resourceMessage + ", ";
256             }
257         }
258 
259         try {
260             FileOutputStream out = new FileOutputStream(filename);
261             PrintStream p = new PrintStream(out);
262 
263             p.println("<pdp_load_status>");
264             p.println("  <input_file_name>" + inputFileName + "</input_file_name>");
265             p.println("  <code>" + code + "</code>");
266             p.println("  <count>" + status.getDetailCount() + "</count>");
267             if (status.getDetailTotal() != null) {
268                 p.println("  <total>" + status.getDetailTotal() + "</total>");
269             }
270             else {
271                 p.println("  <total>0</total>");
272             }
273 
274             p.println("  <description>" + message + "</description>");
275             p.println("  <messages>");
276             for (String warning : status.getWarnings()) {
277                 p.println("    <message>" + warning + "</message>");
278             }
279             p.println("  </messages>");
280             p.println("</pdp_load_status>");
281 
282             p.close();
283             out.close();
284         }
285         catch (FileNotFoundException e) {
286             LOG.error("createOutputFile() Cannot create output file", e);
287             return false;
288         }
289         catch (IOException e) {
290             LOG.error("createOutputFile() Cannot write to output file", e);
291             return false;
292         }
293 
294         return true;
295     }
296 
297     /**
298      * Create a new <code>Batch</code> record for the payment file.
299      * 
300      * @param paymentFile parsed payment file object
301      * @param fileName payment file name (without path)
302      * @return <code>Batch<code> object
303      */
304     protected Batch createNewBatch(PaymentFileLoad paymentFile, String fileName) {
305         Timestamp now = dateTimeService.getCurrentTimestamp();
306 
307         Calendar nowPlus30 = Calendar.getInstance();
308         nowPlus30.setTime(now);
309         nowPlus30.add(Calendar.DATE, 30);
310 
311         Calendar nowMinus30 = Calendar.getInstance();
312         nowMinus30.setTime(now);
313         nowMinus30.add(Calendar.DATE, -30);
314 
315         Batch batch = new Batch();
316 
317         CustomerProfile customer = customerProfileService.get(paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit());
318         batch.setCustomerProfile(customer);
319         batch.setCustomerFileCreateTimestamp(new Timestamp(paymentFile.getCreationDate().getTime()));
320         batch.setFileProcessTimestamp(now);
321         batch.setPaymentCount(new KualiInteger(paymentFile.getPaymentCount()));
322 
323         if (fileName.length() > 30) {
324             batch.setPaymentFileName(fileName.substring(0, 30));
325         }
326         else {
327             batch.setPaymentFileName(fileName);
328         }
329 
330         batch.setPaymentTotalAmount(paymentFile.getPaymentTotalAmount());
331         batch.setSubmiterUserId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
332 
333         return batch;
334     }
335 
336 
337     /**
338      * @returns the file name from the file full path.
339      */
340     protected String getBaseFileName(String filename) {
341         // Replace any backslashes with forward slashes. Works on Windows or Unix
342         filename = filename.replaceAll("\\\\", "/");
343 
344         int startingPointer = filename.length() - 1;
345         while ((startingPointer > 0) && (filename.charAt(startingPointer) != '/')) {
346             startingPointer--;
347         }
348 
349         return filename.substring(startingPointer + 1);
350     }
351 
352     /**
353      * Clears out the associated .done file for the processed data file
354      * 
355      * @param dataFileName the name of date file with done file to remove
356      */
357     protected void removeDoneFile(String dataFileName) {
358         File doneFile = new File(StringUtils.substringBeforeLast(dataFileName, ".") + ".done");
359         if (doneFile.exists()) {
360             doneFile.delete();
361         }
362     }
363 
364     /**
365      * Sets the outgoingDirectoryName attribute value.
366      * 
367      * @param outgoingDirectoryName The outgoingDirectoryName to set.
368      */
369     public void setOutgoingDirectoryName(String outgoingDirectoryName) {
370         this.outgoingDirectoryName = outgoingDirectoryName;
371     }
372 
373     /**
374      * Sets the parameterService attribute value.
375      * 
376      * @param parameterService The parameterService to set.
377      */
378     public void setParameterService(ParameterService parameterService) {
379         this.parameterService = parameterService;
380     }
381 
382     /**
383      * Sets the customerProfileService attribute value.
384      * 
385      * @param customerProfileService The customerProfileService to set.
386      */
387     public void setCustomerProfileService(CustomerProfileService customerProfileService) {
388         this.customerProfileService = customerProfileService;
389     }
390 
391     /**
392      * Sets the batchInputFileService attribute value.
393      * 
394      * @param batchInputFileService The batchInputFileService to set.
395      */
396     public void setBatchInputFileService(BatchInputFileService batchInputFileService) {
397         this.batchInputFileService = batchInputFileService;
398     }
399 
400     /**
401      * Sets the paymentFileValidationService attribute value.
402      * 
403      * @param paymentFileValidationService The paymentFileValidationService to set.
404      */
405     public void setPaymentFileValidationService(PaymentFileValidationService paymentFileValidationService) {
406         this.paymentFileValidationService = paymentFileValidationService;
407     }
408 
409     /**
410      * Sets the businessObjectService attribute value.
411      * 
412      * @param businessObjectService The businessObjectService to set.
413      */
414     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
415         this.businessObjectService = businessObjectService;
416     }
417 
418     /**
419      * Sets the dateTimeService attribute value.
420      * 
421      * @param dateTimeService The dateTimeService to set.
422      */
423     public void setDateTimeService(DateTimeService dateTimeService) {
424         this.dateTimeService = dateTimeService;
425     }
426 
427     /**
428      * Sets the paymentFileEmailService attribute value.
429      * 
430      * @param paymentFileEmailService The paymentFileEmailService to set.
431      */
432     public void setPaymentFileEmailService(PdpEmailService paymentFileEmailService) {
433         this.paymentFileEmailService = paymentFileEmailService;
434     }
435 
436     /**
437      * Sets the kualiConfigurationService attribute value.
438      * 
439      * @param kualiConfigurationService The kualiConfigurationService to set.
440      */
441     public void setConfigurationService(ConfigurationService kualiConfigurationService) {
442         this.kualiConfigurationService = kualiConfigurationService;
443     }
444 
445     /**
446      * @see org.kuali.ole.sys.batch.service.impl.InitiateDirectoryImpl#getRequiredDirectoryNames()
447      */
448     @Override
449     public List<String> getRequiredDirectoryNames() {
450         return new ArrayList<String>() {{add(outgoingDirectoryName); }};
451     }
452 
453 }
454