package fr.acxio.tools.agia.io;
/*
* Copyright 2014 Acxio
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFileFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.core.io.Resource;
/**
* <p>Abstract files manipulation utilities.</p>
* <p>The source resources must be provided by the subclass.</p>
* <p>The destination of a copy or move operation must be a single resource.
* This resource can be built by a {@code ResourceFactory}.</p>
* <p>The destination is built for each source: it can be a single folder or
* different filenames calculated from a complex expression.</p>
* <p>Each source resource and the current StepExecution are sent in the
* evaluation context of the destination factory.</p>
* <p>These objects can be used to calculate a complex path. For example:</p>
*
* <pre>
* ${config.destdir}/@{#in.STEP_EXEC.jobExecution.jobId}/@{#in.SOURCE.filename}
* </pre>
* <p>
* In this expression, the base path is read from a properties file, the folder
* just under the base path is named from the current job instance ID, and the
* source filename is used as the destination filename.
* </p>
* <p>A subclass may provide a default destination Resource in the case of the
* destination ResourceFactory is null.</p>
*
* @author pcollardez
*
*/
public abstract class AbstractFileOperations {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFileOperations.class);
private static final String SAME_RELATIVE_PATH = "/.";
public enum Operation {
COPY, MOVE, REMOVE
}
protected Operation operation;
protected ResourceFactory destinationFactory;
protected boolean preserveAttributes = false;
protected boolean recursive = false;
public Operation getOperation() {
return operation;
}
public void setOperation(Operation sOperation) {
operation = sOperation;
}
public void setDestinationFactory(ResourceFactory sDestinationFactory) {
destinationFactory = sDestinationFactory;
}
public boolean isPreserveAttributes() {
return preserveAttributes;
}
public void setPreserveAttributes(boolean sPreserveAttributes) {
preserveAttributes = sPreserveAttributes;
}
public boolean isRecursive() {
return recursive;
}
public void setRecursive(boolean sRecursive) {
recursive = sRecursive;
}
protected void moveFile(Resource sOriginFile, Resource sDestinationFile) throws IOException {
File aOrigineFile = sOriginFile.getFile();
if (aOrigineFile.isFile()) {
if (!isDirectory(sDestinationFile)) {
FileUtils.moveFile(aOrigineFile, sDestinationFile.getFile());
} else {
FileUtils.moveFileToDirectory(aOrigineFile, sDestinationFile.getFile(), true);
}
} else {
FileUtils.moveDirectoryToDirectory(aOrigineFile, sDestinationFile.getFile(), true);
}
}
protected void copyFile(Resource sOriginFile, Resource sDestinationFile) throws IOException {
File aOrigineFile = sOriginFile.getFile();
if (aOrigineFile.isFile()) {
if (!isDirectory(sDestinationFile)) {
FileUtils.copyFile(aOrigineFile, sDestinationFile.getFile(), preserveAttributes);
} else {
FileUtils.copyFileToDirectory(aOrigineFile, sDestinationFile.getFile(), preserveAttributes);
}
} else {
if (recursive) {
FileUtils.copyDirectory(aOrigineFile, sDestinationFile.getFile(), preserveAttributes);
} else {
FileUtils.copyDirectory(aOrigineFile, sDestinationFile.getFile(), FileFileFilter.FILE, preserveAttributes);
}
}
}
protected void removeFile(Resource sOriginFile) throws IOException {
FileUtils.forceDelete(sOriginFile.getFile());
}
protected boolean isDirectory(Resource sResource) throws IOException {
if (sResource.exists()) {
return sResource.getFile().isDirectory();
} else {
// Dirty trick to tell if the resource is a directory or not when it does not exist yet
return sResource.createRelative(SAME_RELATIVE_PATH).getFile().getCanonicalPath().equals(sResource.getFile().getCanonicalPath());
}
}
protected List<String> doOperation(Resource aSourceResource, Map<String, Object> aDestinationParams, StepContribution sContribution, StepExecution sStepExecution)
throws IOException, ResourceCreationException, FileOperationException {
List<String> aDestinationFilesList = new ArrayList<String>();
File aOriginFile = aSourceResource.getFile();
if (aOriginFile.exists()) {
if (sContribution != null) {
sContribution.incrementReadCount();
}
if (operation == Operation.REMOVE) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Deleting : {}", aOriginFile.getAbsolutePath());
}
removeFile(aSourceResource);
} else {
Resource aDestination = getDefaultDestination();
if (destinationFactory != null) {
aDestinationParams.put(ResourceFactoryConstants.PARAM_SOURCE, aSourceResource);
aDestinationParams.put(ResourceFactoryConstants.PARAM_STEP_EXEC, sStepExecution);
aDestination = destinationFactory.getResource(aDestinationParams);
}
if ((aDestination != null) && (aDestination.getFile() != null)) {
File aDestinationFile = aDestination.getFile();
if (operation == Operation.COPY) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Copying : {} => {}", aOriginFile.getAbsolutePath(), aDestinationFile.getAbsolutePath());
}
copyFile(aSourceResource, aDestination);
} else if (operation == Operation.MOVE) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Moving : {} => {}", aOriginFile.getAbsolutePath(), aDestinationFile.getAbsolutePath());
}
moveFile(aSourceResource, aDestination);
} else {
throw new FileOperationException("Unknown operation");
}
} else {
throw new FileOperationException("No destination specified");
}
aDestinationFilesList.add(aDestination.getFile().getCanonicalPath());
}
if (sContribution != null) {
sContribution.incrementWriteCount(1);
}
} else {
throw new FileOperationException("File not found: " + aOriginFile);
}
return aDestinationFilesList;
}
protected abstract Resource getDefaultDestination();
}