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.gl.batch.service.impl;
17  
18  import java.io.BufferedReader;
19  import java.io.File;
20  import java.io.FileReader;
21  import java.io.IOException;
22  import java.io.PrintStream;
23  import java.io.Reader;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import org.kuali.ole.gl.batch.service.FileEnterpriseFeederHelperService;
28  import org.kuali.ole.gl.batch.service.ReconciliationParserService;
29  import org.kuali.ole.gl.batch.service.ReconciliationService;
30  import org.kuali.ole.gl.businessobject.OriginEntryFull;
31  import org.kuali.ole.gl.report.LedgerSummaryReport;
32  import org.kuali.ole.gl.service.OriginEntryService;
33  import org.kuali.ole.gl.service.impl.EnterpriseFeederStatusAndErrorMessagesWrapper;
34  import org.kuali.ole.sys.Message;
35  
36  /**
37   * This class reads origin entries in a flat file format, reconciles them, and loads them into the origin entry table. 
38   * Note: the feeding algorithm of this service will read the data file twice to minimize memory usage.
39   */
40  public class FileEnterpriseFeederHelperServiceImpl implements FileEnterpriseFeederHelperService {
41      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FileEnterpriseFeederHelperServiceImpl.class);
42  
43      protected ReconciliationParserService reconciliationParserService;
44      protected ReconciliationService reconciliationService;
45      protected OriginEntryService originEntryService;
46  
47      /**
48       * This method does the reading and the loading of reconciliation. Read class description. This method DOES NOT handle the
49       * deletion of the done file
50       * 
51       * @param doneFile a URL that must be present. The contents may be empty
52       * @param dataFile a URL to a flat file of origin entry rows.
53       * @param reconFile a URL to the reconciliation file. See the implementation of {@link ReconciliationParserService} for file
54       *        format.
55       * @param originEntryGroup the group into which the origin entries will be loaded
56       * @param feederProcessName the name of the feeder process
57       * @param reconciliationTableId the name of the block to use for reconciliation within the reconciliation file
58       * @param statusAndErrors any status information should be stored within this object
59       * @see org.kuali.module.gl.service.impl.FileEnterpriseFeederHelperService#feedOnFile(java.io.File, java.io.File, java.io.File,
60       *      org.kuali.ole.gl.businessobject.OriginEntryGroup)
61       */
62      @Override
63      public void feedOnFile(File doneFile, File dataFile, File reconFile, PrintStream enterpriseFeedPs, String feederProcessName, String reconciliationTableId, EnterpriseFeederStatusAndErrorMessagesWrapper statusAndErrors, LedgerSummaryReport ledgerSummaryReport) {
64          if ( LOG.isInfoEnabled() ) {
65          LOG.info("Processing done file: " + doneFile.getAbsolutePath());
66          }
67  
68          List<Message> errorMessages = statusAndErrors.getErrorMessages();
69          BufferedReader dataFileReader = null;
70  
71          ReconciliationBlock reconciliationBlock = null;
72          Reader reconReader = null;
73          try {
74              reconReader = new FileReader(reconFile);
75              reconciliationBlock = reconciliationParserService.parseReconciliationBlock(reconReader, reconciliationTableId);
76          }
77          catch (IOException e) {
78              LOG.error("IO Error occured trying to read the recon file.", e);
79              errorMessages.add(new Message("IO Error occured trying to read the recon file.", Message.TYPE_FATAL));
80              reconciliationBlock = null;
81              statusAndErrors.setStatus(new FileReconBadLoadAbortedStatus());
82              throw new RuntimeException(e);
83          }
84          catch (RuntimeException e) {
85              LOG.error("Error occured trying to parse the recon file.", e);
86              errorMessages.add(new Message("Error occured trying to parse the recon file.", Message.TYPE_FATAL));
87              reconciliationBlock = null;
88              statusAndErrors.setStatus(new FileReconBadLoadAbortedStatus());
89              throw e;
90          }
91          finally {
92              if (reconReader != null) {
93                  try {
94                      reconReader.close();
95                  }
96                  catch (IOException e) {
97                      LOG.error("Error occured trying to close recon file: " + reconFile.getAbsolutePath(), e);
98                  }
99              }
100         }
101 
102         try {
103             if (reconciliationBlock == null) {
104                 errorMessages.add(new Message("Unable to parse reconciliation file.", Message.TYPE_FATAL));
105             }
106             else {
107                 dataFileReader = new BufferedReader(new FileReader(dataFile));
108                 Iterator<OriginEntryFull> fileIterator = new OriginEntryFileIterator(dataFileReader, false);
109                 reconciliationService.reconcile(fileIterator, reconciliationBlock, errorMessages);
110 
111                 fileIterator = null;
112                 dataFileReader.close();
113                 dataFileReader = null;
114             }
115 
116             if (reconciliationProcessSucceeded(errorMessages)) {
117                 dataFileReader = new BufferedReader(new FileReader(dataFile));
118                 String line;
119                 int count = 0;
120                 
121                 // create an entry to temporarily parse each line as it comes in
122                 OriginEntryFull tempEntry = new OriginEntryFull();
123                 while ((line = dataFileReader.readLine()) != null) {
124                     try {
125                         enterpriseFeedPs.printf("%s\n", line);
126                         tempEntry.setFromTextFileForBatch(line, count);
127                         ledgerSummaryReport.summarizeEntry(tempEntry);
128                     } catch (Exception e) {
129                         throw new IOException(e.toString());
130                     }
131                     
132                     count++;
133                 }
134                 dataFileReader.close();
135                 dataFileReader = null;
136 
137                 statusAndErrors.setStatus(new FileReconOkLoadOkStatus());
138             }
139             else {
140                 statusAndErrors.setStatus(new FileReconBadLoadAbortedStatus());
141             }
142         }
143         catch (Exception e) {
144             LOG.error("Caught exception when reconciling/loading done file: " + doneFile, e);
145             statusAndErrors.setStatus(new ExceptionCaughtStatus());
146             errorMessages.add(new Message("Caught exception attempting to reconcile/load done file: " + doneFile + ".  File contents are NOT loaded", Message.TYPE_FATAL));
147             // re-throw the exception rather than returning a value so that Spring will auto-rollback
148             if (e instanceof RuntimeException) {
149                 throw (RuntimeException) e;
150             }
151             else {
152                 // Spring only rolls back when throwing a runtime exception (by default), so we throw a new exception
153                 throw new RuntimeException(e);
154             }
155         }
156         finally {
157             if (dataFileReader != null) {
158                 try {
159                     dataFileReader.close();
160                 }
161                 catch (IOException e) {
162                     LOG.error("IO Exception occured trying to close connection to the data file", e);
163                     errorMessages.add(new Message("IO Exception occured trying to close connection to the data file", Message.TYPE_FATAL));
164                 }
165             }
166         }
167     }
168 
169     /**
170      * Returns whether the reconciliation process succeeded by looking at the reconciliation error messages For this implementation,
171      * the reconciliation does not succeed if at least one of the error messages in the list has a type of
172      * {@link Message#TYPE_FATAL}
173      * 
174      * @param errorMessages a List of errorMessages
175      * @return true if any of those error messages were fatal
176      */
177     protected boolean reconciliationProcessSucceeded(List<Message> errorMessages) {
178         for (Message message : errorMessages) {
179             if (message.getType() == Message.TYPE_FATAL) {
180                 return false;
181             }
182         }
183         return true;
184     }
185 
186     public void setReconciliationParserService(ReconciliationParserService reconciliationParserService) {
187         this.reconciliationParserService = reconciliationParserService;
188     }
189     public void setReconciliationService(ReconciliationService reconciliationService) {
190         this.reconciliationService = reconciliationService;
191     }
192     public void setOriginEntryService(OriginEntryService originEntryService) {
193         this.originEntryService = originEntryService;
194     }
195 }