/**
* 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.
*/
package org.jboss.loom.actions;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.commons.io.FileUtils;
import org.jboss.loom.ex.ActionException;
import org.jboss.loom.ex.MigrationException;
import org.jboss.loom.spi.IMigrator;
import org.jboss.loom.spi.ann.Property;
import org.jboss.loom.utils.DirScanner;
import org.jboss.loom.utils.Utils;
/**
* Base for file-based operations, like copying files, merging properties, or XSLT transformations.
*
* @author Ondrej Zizka, ozizka at redhat.com
*/
public abstract class FileAbstractAction extends AbstractStatefulAction {
// First alternative: exact file.
protected File src;
// Second alternative:
protected String pathMask; // What to look for - Ant-style path mask.
protected File baseDir; // Where to look for.
protected File dest;
protected boolean failIfNotExist = true;
private File temp;
@Override
public String toDescription() {
return this.verb() + " file, " + addToDescription()
+ (this.failIfNotExist ? "" : " don't") + " fail if exists,"
+ "\n from " + (this.src != null ? this.src.getPath() : this.pathMask )
+ "\n to " + (this.dest == null ? "null" : this.dest.getPath());
}
protected abstract String verb();
protected String addToDescription(){ return ""; }
public FileAbstractAction(Class<? extends IMigrator> fromMigrator, File src, File dest) {
super(fromMigrator);
this.src = src;
this.dest = dest;
}
public FileAbstractAction(Class<? extends IMigrator> fromMigrator, File src, File dest, boolean failIfNotExist) {
super(fromMigrator);
this.src = src;
this.dest = dest;
this.failIfNotExist = failIfNotExist;
}
public FileAbstractAction(Class<? extends IMigrator> fromMigrator, String pathMask, File baseDir, File dest, boolean failIfNotExist) {
super(fromMigrator);
this.src = null;
this.baseDir = baseDir;
this.pathMask = pathMask;
this.dest = dest;
this.failIfNotExist = failIfNotExist;
}
@Override
public void preValidate() throws MigrationException {
if( src != null ){
if ( ! src.exists() && failIfNotExist )
throw new ActionException(this, "File to "+ verb().toLowerCase() +" doesn't exist: " + src.getPath());
}
else if( this.pathMask == null ){
throw new ActionException( this, "Neither src nor pathMask is set.");
}
// Copy multiple files to a file?
else {
// We would have to scan for the files now.
}
}
/**
* Copies the dest file, if it exists, to a temp file.
*/
@Override
public void backup() throws MigrationException {
if( ! this.dest.exists() ) return;
try {
this.temp = File.createTempFile(this.dest.getName(), null);
FileUtils.deleteQuietly( this.temp );
Utils.copyFileOrDirectory(this.dest, this.temp);
} catch (IOException ex) {
throw new ActionException(this, "Creating a backup file failed: " + ex.getMessage(), ex);
}
setState(IMigrationAction.State.BACKED_UP);
}
@Override
public void rollback() throws MigrationException {
if( ! this.isAfterPerform() ) return;
// Delete the new file.
FileUtils.deleteQuietly( this.dest );
// Restore the backup file, if we created any.
if( this.temp != null ) {
try {
FileUtils.moveFile(this.temp, this.dest);
} catch (IOException ex) {
throw new ActionException(this, "Restoring the previous file failed: " + ex.getMessage(), ex);
}
}
setState(IMigrationAction.State.ROLLED_BACK);
}
@Override
public void postValidate() throws MigrationException {
// Empty - JRE would give IOEx if something.
}
/**
* Removes the temp file.
*/
@Override
public void cleanBackup() {
if( this.temp == null )
return;
if( this.temp.exists() ) {
FileUtils.deleteQuietly(this.temp);
}
setState(IMigrationAction.State.FINISHED);
}
//<editor-fold defaultstate="collapsed" desc="hash/eq - use src and dest.">
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode( this.src );
hash = 67 * hash + Objects.hashCode( this.dest );
return hash;
}
@Override
public boolean equals( Object obj ) {
if( obj == null ) {
return false;
}
if( getClass() != obj.getClass() ) {
return false;
}
final FileAbstractAction other = (FileAbstractAction) obj;
if( !Objects.equals( this.src, other.src ) ) {
return false;
}
if( !Objects.equals( this.dest, other.dest ) ) {
return false;
}
return true;
}
//</editor-fold>
@Property(name = "src", style = "code", label = "From")
public File getSrc() {
return src;
}
@Property(name = "dest", style = "code", label = "To")
public File getDest() {
return dest;
}
/**
* Finds files according to this.pathMask and this.baseDir.
*/
private List<File> findFilesForPattern() throws IOException {
if( this.pathMask == null )
throw new IllegalStateException("pathMask is null in " + this.toDescription() );
List<File> files = new DirScanner( this.pathMask ).listAsFiles( this.baseDir );
return files;
}
/**
* If $src is defined, returns that. Else returns findFilesForPattern().
*/
public List<File> getFiles() throws IOException {
if( this.src != null )
return Arrays.asList(this.src);
else if( this.pathMask != null )
return findFilesForPattern();
else
return Collections.EMPTY_LIST;
}
}