/*
* Copyright 2013-2016 EMC Corporation. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* or in the "license" file accompanying this file. This file 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 com.emc.ecs.sync.config.storage;
import com.emc.ecs.sync.config.AbstractConfig;
import com.emc.ecs.sync.config.annotation.*;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.emc.ecs.sync.config.storage.FilesystemConfig.URI_PREFIX;
@XmlRootElement
@StorageConfig(uriPrefix = URI_PREFIX)
@Label("Filesystem")
@Documentation("The filesystem plugin reads/writes data from/to a file or directory. " +
"It is triggered by the URI:\n" +
"file://<path>, e.g. file:///home/user/myfiles\n" +
"If the URL refers to a file, only that file will be " +
"synced. If a directory is specified, the contents of " +
"the directory will be synced. Unless the --non-recursive " +
"flag is set, the subdirectories will also be recursively " +
"synced. To preserve object metadata on the target filesystem, " +
"or to read back preserved metadata, use --store-metadata.")
public class FilesystemConfig extends AbstractConfig {
static final String URI_PREFIX = "file:";
private static final Pattern URI_PATTERN = Pattern.compile("^file:(?://)?(.+)$");
protected String path;
private boolean useAbsolutePath = false;
private boolean followLinks = false;
private boolean storeMetadata = false;
private long deleteOlderThan = 0;
private String deleteCheckScript;
private String modifiedSince;
private String[] excludedPaths;
private boolean includeBaseDir = false;
@XmlTransient
@UriGenerator
public String getUri() {
return URI_PREFIX + (path == null ? "" : path);
}
@UriParser
public void setUri(String uri) {
Matcher matcher = URI_PATTERN.matcher(uri);
if (matcher.matches()) {
path = matcher.group(1);
} else {
throw new RuntimeException("invalid file URI");
}
}
@Option(orderIndex = 10, locations = Option.Location.Form, required = true, description = "path to the primary file or directory.")
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Option(orderIndex = 20, advanced = true, description = "uses the absolute path to the file when storing it instead of the relative path from the source dir")
public boolean isUseAbsolutePath() {
return useAbsolutePath;
}
public void setUseAbsolutePath(boolean useAbsolutePath) {
this.useAbsolutePath = useAbsolutePath;
}
@Option(orderIndex = 30, advanced = true, description = "instead of preserving symbolic links, follow them and sync the actual files")
public boolean isFollowLinks() {
return followLinks;
}
public void setFollowLinks(boolean followLinks) {
this.followLinks = followLinks;
}
@Option(orderIndex = 40, advanced = true, description = "when used as a target, stores source metadata in a json file, since filesystems have no concept of user metadata")
public boolean isStoreMetadata() {
return storeMetadata;
}
public void setStoreMetadata(boolean storeMetadata) {
this.storeMetadata = storeMetadata;
}
@Option(orderIndex = 50, valueHint = "delete-age", advanced = true, description = "when --delete-source is used, add this option to only delete files that have been modified more than <delete-age> milliseconds ago")
public long getDeleteOlderThan() {
return deleteOlderThan;
}
public void setDeleteOlderThan(long deleteOlderThan) {
this.deleteOlderThan = deleteOlderThan;
}
@Option(orderIndex = 60, advanced = true, description = "when --delete-source is used, add this option to execute an external script to check whether a file should be deleted. If the process exits with return code zero, the file is safe to delete.")
public String getDeleteCheckScript() {
return deleteCheckScript;
}
public void setDeleteCheckScript(String deleteCheckScript) {
this.deleteCheckScript = deleteCheckScript;
}
@Option(orderIndex = 70, valueHint = "yyyy-MM-ddThh:mm:ssZ", advanced = true, description = "only look at files that have been modified since the specifiec date/time. Date/time should be provided in ISO-8601 UTC format (i.e. 2015-01-01T04:30:00Z)")
public String getModifiedSince() {
return modifiedSince;
}
/**
* Date/time should be provided in ISO-8601 UTC format (i.e. 2015-01-01T04:30:00Z)
*/
public void setModifiedSince(String modifiedSince) {
this.modifiedSince = modifiedSince;
}
@Option(orderIndex = 80, valueHint = "regex-pattern", description = "a list of regular expressions to search against the full file path. If the path matches, the file will be skipped. Since this is a regular expression, take care to escape special characters. For example, to exclude all .snapshot directories, the pattern would be .*/\\.snapshot. Specify multiple entries by repeating the CLI option or using multiple lines in the UI form")
public String[] getExcludedPaths() {
return excludedPaths;
}
public void setExcludedPaths(String[] excludedPaths) {
this.excludedPaths = excludedPaths;
}
@Option(orderIndex = 90, description = "by default, the base directory is not included as part of the sync (only its children are). enable this to sync the base directory")
public boolean isIncludeBaseDir() {
return includeBaseDir;
}
public void setIncludeBaseDir(boolean includeBaseDir) {
this.includeBaseDir = includeBaseDir;
}
}