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.FileInputStream;
20  import java.io.FileOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.util.ArrayList;
25  import java.util.Date;
26  import java.util.HashMap;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.kuali.ole.sys.OLEConstants;
32  import org.kuali.ole.sys.OLEConstants.SystemGroupParameterNames;
33  import org.kuali.ole.sys.batch.BatchInputFileSetType;
34  import org.kuali.ole.sys.batch.InitiateDirectoryBase;
35  import org.kuali.ole.sys.batch.service.BatchInputFileSetService;
36  import org.kuali.ole.sys.context.SpringContext;
37  import org.kuali.ole.sys.exception.FileStorageException;
38  import org.kuali.ole.sys.service.impl.OleParameterConstants;
39  import org.kuali.rice.core.api.config.property.ConfigurationService;
40  import org.kuali.rice.core.api.datetime.DateTimeService;
41  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
42  import org.kuali.rice.kim.api.identity.Person;
43  import org.kuali.rice.krad.exception.AuthorizationException;
44  import org.kuali.rice.krad.exception.ValidationException;
45  import org.kuali.rice.krad.util.GlobalVariables;
46  
47  /**
48   * Base implementation to manipulate batch input file sets from the batch upload screen
49   */
50  public class BatchInputFileSetServiceImpl extends InitiateDirectoryBase implements BatchInputFileSetService {
51      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BatchInputFileSetServiceImpl.class);
52  
53      protected ConfigurationService kualiConfigurationService;
54  
55      /**
56       * Generates the file name of a file (not the done file)
57       * 
58       * @param user the user who uploaded or will upload the file
59       * @param inputType the file set type
60       * @param fileUserIdentifier the file identifier
61       * @param fileType the file type
62       * @return the file name, starting with the directory path
63       */
64      protected String generateFileName(Person user, BatchInputFileSetType inputType, String fileUserIdentifier, String fileType, Date creationDate) {        
65          if (!isFileUserIdentifierProperlyFormatted(fileUserIdentifier)) {
66              throw new IllegalArgumentException("The file set identifier is not properly formatted: " + fileUserIdentifier);
67          }
68          return inputType.getDirectoryPath(fileType) + File.separator + inputType.getFileName(fileType, user.getPrincipalName(), fileUserIdentifier, creationDate);
69      }
70  
71      /**
72       * Generates the file name of a file (not the done file)
73       * 
74       * @param user the user who uploaded or will upload the file
75       * @param inputType the file set type
76       * @param fileUserIdentifier the file identifier
77       * @param fileType the file type
78       * @return the file name, starting with the directory path
79       */
80      protected String generateTempFileName(Person user, BatchInputFileSetType inputType, String fileUserIdentifier, String fileType, Date creationDate) {
81          if (!isFileUserIdentifierProperlyFormatted(fileUserIdentifier)) {
82              throw new IllegalArgumentException("The file set identifier is not properly formatted: " + fileUserIdentifier);
83          }
84          return getTempDirectoryName() + File.separator + inputType.getFileName(fileType, user.getPrincipalName(), fileUserIdentifier, creationDate);
85      }
86      /**
87       * Generates the file name of the done file, if supported by the underlying input type
88       * 
89       * @param user the user who uploaded or will upload the file
90       * @param inputType the file set type
91       * @param fileUserIdentifier the file identifier
92       * @param fileType the file type
93       * @return the file name, starting with the directory path
94       */
95      protected String generateDoneFileName(Person user, BatchInputFileSetType inputType, String fileUserIdentifier, Date creationDate) {        
96          if (!isFileUserIdentifierProperlyFormatted(fileUserIdentifier)) {
97              throw new IllegalArgumentException("The file set identifier is not properly formatted: " + fileUserIdentifier);
98          }
99          return inputType.getDoneFileDirectoryPath() + File.separator + inputType.getDoneFileName(user, fileUserIdentifier, creationDate);
100     }
101 
102     /**
103      * @see org.kuali.ole.sys.batch.service.BatchInputFileSetService#isBatchInputTypeActive(org.kuali.ole.sys.batch.BatchInputFileSetType)
104      */
105     public boolean isBatchInputTypeActive(BatchInputFileSetType batchInputFileSetType) {
106         if (batchInputFileSetType == null) {
107             LOG.error("an invalid(null) argument was given");
108             throw new IllegalArgumentException("an invalid(null) argument was given");
109         }
110         List<String> activeInputTypes = new ArrayList<String>( SpringContext.getBean(ParameterService.class).getParameterValuesAsString(OleParameterConstants.FINANCIAL_SYSTEM_BATCH.class, SystemGroupParameterNames.ACTIVE_INPUT_TYPES_PARAMETER_NAME) );
111         
112         boolean activeBatchType = false;
113         if (activeInputTypes.size() > 0 && activeInputTypes.contains(batchInputFileSetType.getFileSetTypeIdentifer())) {
114             activeBatchType = true;
115         }
116 
117         return activeBatchType;
118     }
119 
120     /**
121      * @see org.kuali.ole.sys.batch.service.BatchInputFileSetService#save(org.kuali.rice.kim.api.identity.Person,
122      *      org.kuali.ole.sys.batch.BatchInputFileSetType, java.lang.String, java.util.Map)
123      */
124     public Map<String, String> save(Person user, BatchInputFileSetType inputType, String fileUserIdentifier, Map<String, InputStream> typeToStreamMap) throws AuthorizationException, FileStorageException {
125         //add a step for file directory checking
126         prepareDirectories(getRequiredDirectoryNames());
127         
128         Date creationDate = SpringContext.getBean(DateTimeService.class).getCurrentDate();
129         // check user is authorized to upload a file for the batch type
130         Map<String, File> typeToTempFiles = copyStreamsToTemporaryDirectory(user, inputType, fileUserIdentifier, typeToStreamMap, creationDate);
131         
132         // null the map, because it's full of exhausted input streams that are useless 
133         typeToStreamMap = null;
134         
135         if (!inputType.validate(typeToTempFiles)) {
136             deleteTempFiles(typeToTempFiles);
137             LOG.error("Upload file validation failed for user " + user.getName() + " identifier " + fileUserIdentifier);
138             throw new ValidationException("File validation failed: " + GlobalVariables.getMessageMap().getErrorMessages());
139         }
140         
141         byte[] buf = new byte[1024];
142 
143         Map<String, String> typeToFileNames = new LinkedHashMap<String, String>();
144         Map<String, File> typeToFiles = new LinkedHashMap<String, File>();
145         try {
146             for (String fileType : inputType.getFileTypes()) {
147                 File tempFile = typeToTempFiles.get(fileType);
148                 String saveFileName = inputType.getDirectoryPath(fileType) + File.separator + tempFile.getName();
149                 try {
150                     InputStream fileContents = new FileInputStream(tempFile);
151                     File fileToSave = new File(saveFileName);
152     
153                     copyInputStreamToFile(fileContents, fileToSave, buf);
154                     fileContents.close();
155                     typeToFileNames.put(fileType, saveFileName);
156                     typeToFiles.put(fileType, fileToSave);
157                 }
158                 catch (IOException e) {
159                     LOG.error("unable to save contents to file " + saveFileName, e);
160                     throw new RuntimeException("errors encountered while writing file " + saveFileName, e);
161                 }
162             }
163         }
164         finally {
165             deleteTempFiles(typeToTempFiles);
166         }
167 
168         String doneFileName = inputType.getDoneFileDirectoryPath() + File.separator + inputType.getDoneFileName(user, fileUserIdentifier, creationDate);
169         File doneFile = new File(doneFileName);
170         try {
171             doneFile.createNewFile();
172             
173             typeToFiles.put(OLEConstants.DONE_FILE_TYPE, doneFile);
174         }
175         catch (IOException e) {
176             LOG.error("unable to create done file", e);
177             throw new RuntimeException("unable to create done file", e);
178         }
179         
180         inputType.process(typeToFiles);
181         
182         return typeToFileNames;
183     }
184 
185     protected Map<String, File> copyStreamsToTemporaryDirectory(Person user, BatchInputFileSetType inputType,
186             String fileUserIdentifier, Map<String, InputStream> typeToStreamMap, Date creationDate) throws FileStorageException {
187         Map<String, File> tempFiles = new HashMap<String, File>();
188         
189         String tempDirectoryName = getTempDirectoryName();
190         File tempDirectory = new File(tempDirectoryName);
191         if (!tempDirectory.exists() || !tempDirectory.isDirectory()) {
192             LOG.error("Temporary Directory " + tempDirectoryName + " does not exist or is not a directory");
193             throw new RuntimeException("Temporary Directory " + tempDirectoryName + " does not exist or is not a directory");
194         }
195 
196         byte[] buf = new byte[1024];
197 
198         try {
199             for (String fileType : inputType.getFileTypes()) {
200                 String tempFileName = generateTempFileName(user, inputType, fileUserIdentifier, fileType, creationDate);
201                 InputStream source = typeToStreamMap.get(fileType);
202                 File tempFile = new File(tempFileName);
203                 copyInputStreamToFile(source, tempFile, buf);
204                 tempFiles.put(fileType, tempFile);
205             }
206         }
207         catch (IOException e) {
208             LOG.error("Error creating temporary files", e);
209             throw new FileStorageException("Error creating temporary files",e);
210             
211         }
212         return tempFiles;
213     }
214 
215     protected void copyInputStreamToFile(InputStream source, File outputFile, byte[] buf) throws IOException {
216         OutputStream out = new FileOutputStream(outputFile);
217         int readBytes = source.read(buf);
218         while (readBytes != -1) {
219             out.write(buf, 0, readBytes);
220             readBytes = source.read(buf);
221         }
222         out.flush();
223         out.close();
224     }
225     
226     protected String getTempDirectoryName() {
227         return kualiConfigurationService.getPropertyValueAsString(OLEConstants.TEMP_DIRECTORY_KEY);
228     }
229 
230     /**
231      * @see org.kuali.ole.sys.batch.service.BatchInputFileSetService#isFileUserIdentifierProperlyFormatted(java.lang.String)
232      */
233     public boolean isFileUserIdentifierProperlyFormatted(String fileUserIdentifier) {
234         if (fileUserIdentifier == null) {
235             return false;
236         }
237         
238         for (int i = 0; i < fileUserIdentifier.length(); i++) {
239             char c = fileUserIdentifier.charAt(i);
240             if (!(Character.isLetterOrDigit(c))) {
241                 return false;
242             }
243         }
244         return true;
245     }
246 
247     public void setConfigurationService(ConfigurationService kualiConfigurationService) {
248         this.kualiConfigurationService = kualiConfigurationService;
249     }
250     
251     protected void deleteTempFiles(Map<String, File> typeToTempFiles) {
252         for (File tempFile : typeToTempFiles.values()) {
253             if (tempFile.exists()) {
254                 boolean deletionSuccessful = tempFile.delete();
255                 if (!deletionSuccessful) {
256                     LOG.error("Unable to delete file (possibly due to unclosed InputStream or Reader on the file): " + tempFile.getAbsolutePath());
257                 }
258             }
259         }
260     }
261 
262     /**
263      * @see org.kuali.ole.sys.batch.service.impl.InitiateDirectoryImpl#getRequiredDirectoryNames()
264      */
265     @Override
266     public List<String> getRequiredDirectoryNames() {
267         return new ArrayList<String>(){{add(getTempDirectoryName());}};
268     }
269 }
270