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.sys.batch.service.impl;
17  
18  import java.io.File;
19  import java.io.FileOutputStream;
20  import java.io.FileWriter;
21  import java.io.FilenameFilter;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.ole.sys.OLEConstants.SystemGroupParameterNames;
29  import org.kuali.ole.sys.batch.BatchInputFileType;
30  import org.kuali.ole.sys.batch.service.BatchInputFileService;
31  import org.kuali.ole.sys.context.SpringContext;
32  import org.kuali.ole.sys.exception.FileStorageException;
33  import org.kuali.ole.sys.exception.ParseException;
34  import org.kuali.ole.sys.service.impl.OleParameterConstants;
35  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
36  import org.kuali.rice.kim.api.identity.Person;
37  import org.kuali.rice.krad.exception.AuthorizationException;
38  import org.kuali.rice.krad.util.ObjectUtils;
39  
40  /**
41   * Provides batch input file management, including listing files, parsing, downloading, storing, and deleting.
42   */
43  public class BatchInputFileServiceImpl implements BatchInputFileService {
44      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BatchInputFileServiceImpl.class);
45  
46      /**
47       * Delegates to the batch input file type to parse the file.
48       *
49       * @see org.kuali.ole.sys.batch.service.BatchInputFileService#parse(org.kuali.ole.sys.batch.BatchInputFileType, byte[])
50       */
51      @Override
52      public Object parse(BatchInputFileType batchInputFileType, byte[] fileByteContent) {
53          try {
54              return batchInputFileType.parse(fileByteContent);
55          }
56          catch (ParseException e) {
57              LOG.error("Error encountered parsing file", e);
58              throw e;
59          }
60      }
61  
62      /**
63       * Defers to batch type to do any validation on the parsed contents.
64       *
65       * @see org.kuali.ole.sys.batch.service.BatchInputFileService#validate(org.kuali.ole.sys.batch.BatchInputFileType, java.lang.Object)
66       */
67      @Override
68      public boolean validate(BatchInputFileType batchInputFileType, Object parsedObject) {
69          if (batchInputFileType == null || parsedObject == null) {
70              LOG.error("an invalid(null) argument was given");
71              throw new IllegalArgumentException("an invalid(null) argument was given");
72          }
73  
74          boolean contentsValid = true;
75          contentsValid = batchInputFileType.validate(parsedObject);
76          return contentsValid;
77      }
78  
79      /**
80       * @see org.kuali.ole.sys.batch.service.BatchInputFileService#save(org.kuali.rice.kim.api.identity.Person,
81       *      org.kuali.ole.sys.batch.BatchInputFileType, java.lang.String, java.io.InputStream)
82       */
83      @Override
84      public String save(Person user, BatchInputFileType batchInputFileType, String fileUserIdentifier, InputStream fileContents, Object parsedObject) throws AuthorizationException, FileStorageException {
85          if (user == null || batchInputFileType == null || fileContents == null) {
86              LOG.error("an invalid(null) argument was given");
87              throw new IllegalArgumentException("an invalid(null) argument was given");
88          }
89  
90          if (!isFileUserIdentifierProperlyFormatted(fileUserIdentifier)) {
91              LOG.error("The following file user identifer was not properly formatted: " + fileUserIdentifier);
92              throw new IllegalArgumentException("The following file user identifer was not properly formatted: " + fileUserIdentifier);
93          }
94  
95          // defer to batch input type to add any security or other needed information to the file name
96          String saveFileName = batchInputFileType.getDirectoryPath() + "/" + batchInputFileType.getFileName(user.getPrincipalName(), parsedObject, fileUserIdentifier);
97          if (!StringUtils.isBlank(batchInputFileType.getFileExtension())) {
98              saveFileName += "." + batchInputFileType.getFileExtension();
99          }
100 
101         // consruct the file object and check for existence
102         File fileToSave = new File(saveFileName);
103         if (fileToSave.exists()) {
104             LOG.error("cannot store file, name already exists " + saveFileName);
105             throw new FileStorageException("Cannot store file because the name " + saveFileName + " already exists on the file system.");
106         }
107 
108         try {
109             FileOutputStream fos = new FileOutputStream(fileToSave);
110             while(fileContents.available() > 0) {
111                 fos.write(fileContents.read());
112             }
113             fos.flush();
114             fos.close();
115 
116             createDoneFile(fileToSave, batchInputFileType);
117 
118             batchInputFileType.process(saveFileName, parsedObject);
119         }
120         catch (IOException e) {
121             LOG.error("unable to save contents to file " + saveFileName, e);
122             throw new RuntimeException("errors encountered while writing file " + saveFileName, e);
123         }
124 
125         return saveFileName;
126     }
127 
128     /**
129      * @see org.kuali.ole.sys.batch.service.BatchInputFileService#save(org.kuali.rice.kim.api.identity.Person,
130      *      org.kuali.ole.sys.batch.BatchInputFileType, java.lang.String, java.io.InputStream)
131      */
132     @Override
133     public String save(Person user, BatchInputFileType batchInputFileType, String fileUserIdentifier, InputStream fileContents, Object parsedObject,String destinationFilePath,String extension) throws AuthorizationException, FileStorageException {
134         if (user == null || batchInputFileType == null || fileContents == null) {
135             LOG.error("an invalid(null) argument was given");
136             throw new IllegalArgumentException("an invalid(null) argument was given");
137         }
138 
139         if (!isFileUserIdentifierProperlyFormatted(fileUserIdentifier)) {
140             LOG.error("The following file user identifer was not properly formatted: " + fileUserIdentifier);
141             throw new IllegalArgumentException("The following file user identifer was not properly formatted: " + fileUserIdentifier);
142         }
143 
144         // defer to batch input type to add any security or other needed information to the file name
145         String os = System.getProperty("os.name");
146         if (LOG.isDebugEnabled()){
147             LOG.debug("batchInputFileType.getDirectoryPath()"+batchInputFileType.getDirectoryPath());
148             LOG.debug("destinationFilePath"+destinationFilePath);
149             LOG.debug("--------------file.Separator---------------"+os.toUpperCase());
150         }
151         String separator = "";
152         if(os.toUpperCase().contains("WIN")){
153             separator = "\\";
154         }else {
155             separator = "/";
156         }
157        String directory = batchInputFileType.getDirectoryPath();
158        String[] directoryPath = directory.toLowerCase().split("staging");
159        String orderType = directoryPath[0]+destinationFilePath;
160        if (LOG.isDebugEnabled()){
161             LOG.debug("DirectoryPath" + directoryPath[0]);
162             LOG.debug("DirectoryPath" + batchInputFileType.getDirectoryPath());
163             LOG.debug("orderType" + orderType);
164        }
165          String  saveFileName = orderType + "/" + batchInputFileType.getFileName(user.getPrincipalName(), parsedObject, fileUserIdentifier,destinationFilePath);
166                  saveFileName += "." + extension;//batchInputFileType.getFileExtension();
167 
168        // consruct the file object and check for existence
169         File fileToSave = new File(saveFileName);
170         if (fileToSave.exists()) {
171             LOG.error("cannot store file, name already exists " + saveFileName);
172             throw new FileStorageException("Cannot store file because the name " + saveFileName + " already exists on the file system.");
173         }
174 
175         try {
176             FileWriter fileWriter = new FileWriter(fileToSave);
177             while (fileContents.available() > 0) {
178                 fileWriter.write(fileContents.read());
179             }
180             fileWriter.flush();
181             fileWriter.close();
182 
183             createDoneFile(fileToSave, batchInputFileType);
184 
185             batchInputFileType.process(saveFileName, parsedObject);
186         }
187         catch (IOException e) {
188             LOG.error("unable to save contents to file " + saveFileName, e);
189             throw new RuntimeException("errors encountered while writing file " + saveFileName, e);
190         }
191 
192         return saveFileName;
193     }
194 
195     /**
196      * Creates a '.done' file with the name of the batch file.
197      */
198     protected void createDoneFile(File batchFile ,BatchInputFileType batchInputFileType ) {
199         String fileExtension = batchInputFileType.getFileExtension();
200         File doneFile = generateDoneFileObject(batchFile, fileExtension);
201         String doneFileName = doneFile.getName();
202 
203         if (!doneFile.exists()) {
204             boolean doneFileCreated = false;
205             try {
206                 doneFileCreated = doneFile.createNewFile();
207             }
208             catch (IOException e) {
209                 LOG.error("unable to create done file " + doneFileName, e);
210                 throw new RuntimeException("Errors encountered while saving the file: Unable to create .done file " + doneFileName, e);
211             }
212 
213             if (!doneFileCreated) {
214                 LOG.error("unable to create done file " + doneFileName);
215                 throw new RuntimeException("Errors encountered while saving the file: Unable to create .done file " + doneFileName);
216             }
217         }
218     }
219 
220     /**
221      * This method is responsible for creating a File object that represents the done file. The real file represented on disk may
222      * not exist
223      *
224      * @param batchInputFile
225      * @return a File object representing the done file. The real file may not exist on disk, but the return value can be used to
226      *         create that file.
227      */
228     protected File generateDoneFileObject(File batchInputFile, String fileExtension) {
229         String doneFileName = fileExtension != null  ? StringUtils.substringBeforeLast(batchInputFile.getPath(), ".") + ".done" :
230                                 batchInputFile.getPath() + ".done" ;
231         File doneFile = new File(doneFileName);
232         return doneFile;
233     }
234 
235     /**
236      * @see org.kuali.ole.sys.batch.service.BatchInputFileService#isBatchInputTypeActive(org.kuali.ole.sys.batch.BatchInputFileType)
237      */
238     @Override
239     public boolean isBatchInputTypeActive(BatchInputFileType batchInputFileType) {
240         if (batchInputFileType == null) {
241             LOG.error("an invalid(null) argument was given");
242             throw new IllegalArgumentException("an invalid(null) argument was given");
243         }
244 
245         List<String> activeInputTypes = new ArrayList<String>( SpringContext.getBean(ParameterService.class).getParameterValuesAsString(OleParameterConstants.FINANCIAL_SYSTEM_BATCH.class, SystemGroupParameterNames.ACTIVE_INPUT_TYPES_PARAMETER_NAME) );
246 
247         boolean activeBatchType = false;
248         if (activeInputTypes.size() > 0 && activeInputTypes.contains(batchInputFileType.getFileTypeIdentifer())) {
249             activeBatchType = true;
250         }
251 
252         return activeBatchType;
253     }
254 
255     /**
256      * Fetches workgroup for batch type from system parameter and verifies user is a member. Then a list of all files for the batch
257      * type are retrieved. For each file, the file and user is sent through the checkAuthorization method of the batch input type
258      * implementation for finer grained security. If the method returns true, the filename is added to the user's list.
259      *
260      * @see org.kuali.ole.sys.batch.service.BatchInputFileService#listBatchTypeFilesForUser(org.kuali.ole.sys.batch.BatchInputFileType,
261      *      org.kuali.rice.kim.api.identity.Person)
262      */
263     @Override
264     public List<String> listBatchTypeFilesForUser(BatchInputFileType batchInputFileType, Person user) throws AuthorizationException {
265         if (batchInputFileType == null || user == null) {
266             LOG.error("an invalid(null) argument was given");
267             throw new IllegalArgumentException("an invalid(null) argument was given");
268         }
269 
270         File[] filesInBatchDirectory = listFilesInBatchTypeDirectory(batchInputFileType);
271 
272         List<String> userFileNamesList = new ArrayList<String>();
273         List<File> userFileList = listBatchTypeFilesForUserAsFiles(batchInputFileType, user);
274 
275         for (File userFile : userFileList) {
276             userFileNamesList.add(userFile.getAbsolutePath());
277         }
278 
279         return userFileNamesList;
280     }
281 
282     protected List<File> listBatchTypeFilesForUserAsFiles(BatchInputFileType batchInputFileType, Person user) throws AuthorizationException {
283         File[] filesInBatchDirectory = listFilesInBatchTypeDirectory(batchInputFileType);
284 
285         List<File> userFileList = new ArrayList<File>();
286         if (filesInBatchDirectory != null) {
287             for (int i = 0; i < filesInBatchDirectory.length; i++) {
288                 File batchFile = filesInBatchDirectory[i];
289                 String fileExtension = StringUtils.substringAfterLast(batchFile.getName(), ".");
290                 if (StringUtils.isBlank(batchInputFileType.getFileExtension()) || batchInputFileType.getFileExtension().equals(fileExtension)) {
291                     if (user.getPrincipalName().equals(batchInputFileType.getAuthorPrincipalName(batchFile))) {
292                         userFileList.add(batchFile);
293                     }
294                 }
295             }
296         }
297         return userFileList;
298     }
299 
300     /**
301      * Returns List of filenames for existing files in the directory given by the batch input type.
302      */
303     protected File[] listFilesInBatchTypeDirectory(BatchInputFileType batchInputFileType) {
304         File batchTypeDirectory = new File(batchInputFileType.getDirectoryPath());
305         return batchTypeDirectory.listFiles();
306     }
307 
308     /**
309      * @see org.kuali.ole.sys.batch.service.BatchInputFileService#listInputFileNamesWithDoneFile(org.kuali.ole.sys.batch.BatchInputFileType)
310      */
311     @Override
312     public List<String> listInputFileNamesWithDoneFile(BatchInputFileType batchInputFileType) {
313         if (batchInputFileType == null) {
314             LOG.error("an invalid(null) argument was given");
315             throw new IllegalArgumentException("an invalid(null) argument was given");
316         }
317 
318         File batchTypeDirectory = new File(batchInputFileType.getDirectoryPath());
319         File[] doneFiles = batchTypeDirectory.listFiles(new DoneFilenameFilter());
320 
321         if(LOG.isDebugEnabled()){
322             LOG.debug("batchTypeDirectory ------------------------------------------->" + batchTypeDirectory);
323             LOG.debug("batchInputFileType ------------------------------------------->" + batchInputFileType);
324             LOG.debug("----------------Done File ----------------------->" + doneFiles);
325         }
326       //  LOG.info( "doneFiles.length --------------------------------------------->"+doneFiles.length);
327         List<String> batchInputFiles = new ArrayList<String>();
328         if(doneFiles==null) {
329             return batchInputFiles;
330         }
331         if(doneFiles != null){
332             LOG.debug("----------Done File Not Null-----------");
333             for (int i = 0; i < doneFiles.length; i++) {
334                 File doneFile = doneFiles[i];
335 
336                 String dataFileName = StringUtils.substringBeforeLast(doneFile.getPath(), ".");
337                 if (!StringUtils.isBlank(batchInputFileType.getFileExtension())) {
338                     dataFileName += "." + batchInputFileType.getFileExtension();
339                 }
340                 File dataFile = new File(dataFileName);
341                 if(LOG.isDebugEnabled()){
342                     LOG.debug("doneFile ------------------------------------------->" + doneFile);
343                     LOG.debug("DataFile being processed ------------------------------------------->" + dataFile);
344                 }
345 
346                 if (dataFile.exists()) {
347                     batchInputFiles.add(dataFile.getPath());
348                 }
349             }
350         }
351 
352         return batchInputFiles;
353     }
354 
355     /**
356      * Retrieves files in a directory with the .done extension.
357      */
358     protected class DoneFilenameFilter implements FilenameFilter {
359         /**
360          * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
361          */
362         @Override
363         public boolean accept(File dir, String name) {
364             return name.endsWith(".done");
365         }
366     }
367 
368     /**
369      * For this implementation, a file user identifier must consist of letters and digits
370      *
371      * @see org.kuali.ole.sys.batch.service.BatchInputFileService#isFileUserIdentifierProperlyFormatted(java.lang.String)
372      */
373     @Override
374     public boolean isFileUserIdentifierProperlyFormatted(String fileUserIdentifier) {
375         if(ObjectUtils.isNull(fileUserIdentifier)) {
376             return false;
377         }
378         for (int i = 0; i < fileUserIdentifier.length(); i++) {
379             char c = fileUserIdentifier.charAt(i);
380             if (!(Character.isLetterOrDigit(c))) {
381                 return false;
382             }
383         }
384         return true;
385     }
386 }
387