/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.sys.batch; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSKeyConstants; import org.kuali.kfs.sys.context.BatchStepFileDescriptor; import org.kuali.kfs.sys.exception.ParseException; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.MessageMap; /** * This class allows the batch process semaphore files to be uploaded. Only the .error files are currently configured for uploading. * To enable the other file types to be uploaded configure a spring bean for each type supplying the * directoryPath, the fileExtension, and fileTypeIdentifier properties */ public class SemaphoreInputFileType extends BatchInputFileTypeBase { private static Logger LOG = Logger.getLogger(SemaphoreInputFileType.class); private String fileTypeIdentifier; /** * @param principalName - unused * @param parsedFileContents List<String> in which the first line is the name of the Job and the Step (JobName~StepName) * @param fileUserIdentifier is not used * @return first line of the parsedFileContents which should be in the form jobName~stepName * * @see org.kuali.kfs.sys.batch.BatchInputFileType#getFileName(String, Object, String) */ @Override public String getFileName(String principalName, Object parsedFileContents, String fileUserIdentifier) { String fileName = ""; if (!validate(parsedFileContents)) { fileName += "invalidJob"+ BatchStepFileDescriptor.STEP_FILE_NAME_SEPARATOR +"invalidStep"; } else { List<String> content = getParsedFileContent(parsedFileContents); String firstLine = content.get(0); fileName = firstLine; } return fileName; } /** * @see org.kuali.kfs.sys.batch.BatchInputFileType#getFileTypeIdentifer() */ @Override public String getFileTypeIdentifer() { return fileTypeIdentifier; } public void setFileTypeIdentifier(String fileTypeIdentifier) { this.fileTypeIdentifier = fileTypeIdentifier; } /** * @see org.kuali.kfs.sys.batch.BatchInputType#getTitleKey() */ @Override public String getTitleKey() { return KFSKeyConstants.Semaphore.MESSAGE_BATCH_UPLOAD_TITLE_PREFIX +"."+ getFileExtension(); } /** * Returns a List<String> in which the first line is the Step and all subsequent lines are considered part of the error message. * * @see org.kuali.kfs.sys.batch.BatchInputFileType#parse(byte[]) */ @Override public Object parse(byte[] fileByteContent) throws ParseException { BufferedReader bufferedFileReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(fileByteContent))); List<String> content = new ArrayList<String>(); try { String fileLine = ""; while ((fileLine = bufferedFileReader.readLine()) != null) { content.add(fileLine); } }catch (Exception e) { LOG.error("An unexpected error occurred while parsing the file: ", e); throw new ParseException("An unexpected error occurred while parsing the file: "+ e.getMessage(), e); } return content; } /** * Remove the done file from the file system if it exists. The done file is not used by the batch process. * * @param fileName the absolute path, file name, and extension of the file * @param parsedFileContents * * @see org.kuali.kfs.sys.batch.BatchInputFileType#process(String, Object) */ @Override public void process(String fileName, Object parsedFileContents) { //remove done file File doneFile = generateDoneFileObject(fileName); if (doneFile.exists()) { LOG.info("Removing the .done file for "+ fileName); if (!doneFile.delete()) { LOG.error("Unable to delete the .done file for "+ fileName); MessageMap errors = new MessageMap(); errors.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.Semaphore.ERROR_BATCH_UPLOAD_DELETE_DONE_FILE); GlobalVariables.mergeErrorMap(errors); } } } /** * Validate that the first line of the file contains a job name and a valid Step (the bean id) * in the format jobName.stepName * * @see org.kuali.kfs.sys.batch.BatchInputFileType#validate(Object) */ @Override public boolean validate(Object parsedFileContents) { List<String> content = getParsedFileContent(parsedFileContents); if (content.size() <= 0) { MessageMap errors = new MessageMap(); errors.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.Semaphore.ERROR_BATCH_UPLOAD_INVALID_STEP); GlobalVariables.mergeErrorMap(errors); return false; } String firstLine = content.get(0); String stepName = StringUtils.substring(firstLine, StringUtils.lastIndexOf(firstLine, BatchStepFileDescriptor.STEP_FILE_NAME_SEPARATOR)+1); Step step = BatchSpringContext.getStep(stepName); if (step == null) { LOG.error("Unable to find bean for step: "+ stepName); MessageMap errors = new MessageMap(); errors.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.Semaphore.ERROR_BATCH_UPLOAD_INVALID_STEP); GlobalVariables.mergeErrorMap(errors); return false; } return true; } /** * The author principal name is not added to the name of the file or in the file contents * @return an empty String * * @see org.kuali.kfs.sys.batch.BatchInputFileType#getAuthorPrincipalName(File) */ @Override public String getAuthorPrincipalName(File file) { return ""; } /** * This method is responsible for creating a File object that represents the done file. The real file represented on disk may * not exist * * @param batchInputFileName * @return a File object representing the done file. The real file may not exist on disk, but the return value can be used to * create that file. */ private File generateDoneFileObject(String batchInputFileName) { String doneFileName = StringUtils.substringBeforeLast(batchInputFileName, ".") + ".done"; File doneFile = new File(doneFileName); return doneFile; } @SuppressWarnings("unchecked") private List<String> getParsedFileContent(Object parsedFileContent) { List<String> content = (List<String>)parsedFileContent; return content; } }