/* ==================================================================
* FileBackupResourceProvider.java - Mar 28, 2013 8:42:37 PM
*
* Copyright 2007-2013 SolarNetwork.net Dev Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ==================================================================
*/
package net.solarnetwork.node.backup;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import net.solarnetwork.node.Constants;
/**
* {@link BackupResourceProvider} for node files, such as installed application
* bundle JARs.
*
* <p>
* The configurable properties of this class are:
* </p>
*
* <dl class="class-properties">
* <dt>rootPath</dt>
* <dd>The root path from which {@code resourceDirectories} are relative to. If
* not provided, the runtime working directory will be used.</dd>
* <dt>resourceDirectories</dt>
* <dd>An array of directory paths, relative to {@code rootPath}, to look for
* files to include in the backup. Defaults to
* {@code [app/base, app/main]}.</dd>
*
* <dt>fileNamePattern</dt>
* <dd>A regexp used to match against files found in the
* {@code resourceDirectories}. Only files matching this expression are included
* in the backup. Defaults to {@code \.jar$}.</dd>
* </dl>
*
* @author matt
* @version 1.1
*/
public class FileBackupResourceProvider implements BackupResourceProvider {
private String rootPath = System.getProperty(Constants.SYSTEM_PROP_NODE_HOME, "");
private String[] resourceDirectories = new String[] { "app/base", "app/main" };
private String fileNamePattern = "\\.jar$";
private MessageSource messageSource;
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public String getKey() {
return FileBackupResourceProvider.class.getName();
}
@Override
public Iterable<BackupResource> getBackupResources() {
if ( resourceDirectories == null || resourceDirectories.length < 1 ) {
return Collections.emptyList();
}
final Pattern pat = (fileNamePattern == null ? null
: Pattern.compile(fileNamePattern, Pattern.CASE_INSENSITIVE));
List<BackupResource> fileList = new ArrayList<BackupResource>(20);
for ( String path : resourceDirectories ) {
File rootDir = (rootPath != null && rootPath.length() > 0 ? new File(rootPath, path)
: new File(path));
if ( !rootDir.isDirectory() ) {
log.info("Skipping path {} because does not exist or is not a directory",
rootDir.getAbsolutePath());
continue;
}
File[] files = rootDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return pat.matcher(name).find();
}
});
if ( files == null || files.length < 1 ) {
continue;
}
for ( File f : files ) {
// make sure backup path is relative
final String backupPath = path + '/' + f.getName();
fileList.add(
new ResourceBackupResource(new FileSystemResource(f), backupPath, getKey()));
}
}
return fileList;
}
@Override
public boolean restoreBackupResource(BackupResource resource) {
if ( resourceDirectories != null && resourceDirectories.length > 0 ) {
for ( String path : resourceDirectories ) {
File rootDir = new File(rootPath, path);
File backupFile = new File(resource.getBackupPath());
if ( backupFile.getAbsolutePath().startsWith(rootDir.getAbsolutePath()) ) {
if ( !backupFile.getParentFile().isDirectory()
&& !backupFile.getParentFile().mkdirs() ) {
log.warn("Unable to create directory {} to restore file {} into",
backupFile.getParent(), resource.getBackupPath());
}
try {
FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(backupFile));
backupFile.setLastModified(resource.getModificationDate());
} catch ( IOException e ) {
log.error("Unable to restore backup resource {} to {}: {}",
resource.getBackupPath(), rootDir.getPath(), e.getMessage());
}
return true;
}
}
}
return false;
}
@Override
public BackupResourceProviderInfo providerInfo(Locale locale) {
String name = "File Backup Provider";
String desc = "Backs up system plugins.";
MessageSource ms = messageSource;
if ( ms != null ) {
name = ms.getMessage("title", null, name, locale);
desc = ms.getMessage("desc", null, desc, locale);
}
return new SimpleBackupResourceProviderInfo(getKey(), name, desc);
}
@Override
public BackupResourceInfo resourceInfo(BackupResource resource, Locale locale) {
return new SimpleBackupResourceInfo(resource.getProviderKey(), resource.getBackupPath(), null);
}
/**
* Set the {@code resourceDirectories} property as a comma-delimieted
* string.
*
* @param list
* a comma-delimited list of paths
*/
public void setResourceDirs(String list) {
setResourceDirectories(StringUtils.commaDelimitedListToStringArray(list));
}
/**
* Get a comma-delimited string of the configured
* {@code resourceDirectories} property.
*
* @return a comma-delimited list of paths
*/
public String getResourceDirs() {
return StringUtils.arrayToCommaDelimitedString(getResourceDirectories());
}
public void setRootPath(String rootPath) {
this.rootPath = rootPath;
}
public void setResourceDirectories(String[] bundlePaths) {
this.resourceDirectories = bundlePaths;
}
public void setFileNamePattern(String fileNamePattern) {
this.fileNamePattern = fileNamePattern;
}
public String getRootPath() {
return rootPath;
}
public String[] getResourceDirectories() {
return resourceDirectories;
}
public String getFileNamePattern() {
return fileNamePattern;
}
/**
* Set the {@link MessageSource} to resolve localized messages with.
*
* @param messageSource
* The message source.
*/
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
}