org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.java

Source

/*
 * Copyright 2010-2013 the original author or authors.
 *
 * 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.springframework.data.gemfire.snapshot;

import static com.gemstone.gemfire.cache.snapshot.SnapshotOptions.SnapshotFormat;
import static org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.SnapshotServiceAdapter;

import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationListener;
import org.springframework.data.gemfire.snapshot.event.ExportSnapshotApplicationEvent;
import org.springframework.data.gemfire.snapshot.event.SnapshotApplicationEvent;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.snapshot.CacheSnapshotService;
import com.gemstone.gemfire.cache.snapshot.RegionSnapshotService;
import com.gemstone.gemfire.cache.snapshot.SnapshotFilter;
import com.gemstone.gemfire.cache.snapshot.SnapshotOptions;

/**
 * The SnapshotServiceFactoryBean class is a Spring FactoryBean used to configure and create an instance
 * of an appropriate GemFire Snapshot Service to perform data import and exports.  A CacheSnapshotService is created
 * if the Region is not specified, otherwise a RegionSnapshotService is used based on the configured Region.
 *
 * @author John Blum
 * @see org.springframework.beans.factory.DisposableBean
 * @see org.springframework.beans.factory.FactoryBean
 * @see org.springframework.beans.factory.InitializingBean
 * @see org.springframework.context.ApplicationListener
 * @see org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.SnapshotServiceAdapter
 * @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService
 * @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService
 * @since 1.7.0
 */
@SuppressWarnings("unused")
public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotServiceAdapter<K, V>>,
        InitializingBean, DisposableBean, ApplicationListener<SnapshotApplicationEvent<K, V>> {

    protected static final SnapshotMetadata[] EMPTY_ARRAY = new SnapshotMetadata[0];

    private Boolean suppressImportOnInit;

    private Cache cache;

    private Region<K, V> region;

    private SnapshotMetadata<K, V>[] exports;
    private SnapshotMetadata<K, V>[] imports;

    private SnapshotServiceAdapter<K, V> snapshotServiceAdapter;

    /* (non-Javadoc) */
    @SuppressWarnings("unchecked")
    static <K, V> SnapshotMetadata<K, V>[] nullSafeArray(SnapshotMetadata<K, V>[] configurations) {
        return (configurations != null ? configurations : EMPTY_ARRAY);
    }

    /* (non-Javadoc) */
    static boolean nullSafeIsDirectory(File file) {
        return (file != null && file.isDirectory());
    }

    /* (non-Javadoc) */
    static boolean nullSafeIsFile(File file) {
        return (file != null && file.isFile());
    }

    /**
     * Sets a reference to the GemFire Cache for which the snapshot will be taken.
     *
     * @param cache the GemFire Cache used to create an instance of CacheSnapshotService.
     * @throws IllegalArgumentException if the Cache reference is null.
     * @see com.gemstone.gemfire.cache.Cache
     * @see #getCache()
     */
    public void setCache(Cache cache) {
        Assert.notNull(cache, "The GemFire Cache must not be null");
        this.cache = cache;
    }

    /**
     * Gets a reference to the GemFire Cache for which the snapshot will be taken.
     *
     * @return the GemFire Cache used to create an instance of CacheSnapshotService.
     * @throws IllegalStateException if the Cache argument is null.
     * @see com.gemstone.gemfire.cache.Cache
     * @see #setCache(Cache)
     */
    protected Cache getCache() {
        Assert.state(cache != null, "The GemFire Cache was not properly initialized");
        return cache;
    }

    /**
     * Sets the meta-data (location, filter and format) used to create a snapshot from the Cache or Region data.
     *
     * @param exports an array of snapshot meta-data used for each export.
     * @see SnapshotServiceFactoryBean.SnapshotMetadata
     */
    public void setExports(SnapshotMetadata<K, V>[] exports) {
        this.exports = exports;
    }

    /**
     * Sets the meta-data (location, filter and format) used to create a snapshot from the Cache or Region data.
     *
     * @return an array of snapshot meta-data used for each export.
     * @see SnapshotServiceFactoryBean.SnapshotMetadata
     */
    protected SnapshotMetadata<K, V>[] getExports() {
        return nullSafeArray(exports);
    }

    /**
     * Sets the meta-data (location, filter and format) used to read a data snapshot into an entire Cache
     * or individual Region.
     *
     * @param imports an array of snapshot meta-data used for each import.
     * @see SnapshotServiceFactoryBean.SnapshotMetadata
     */
    public void setImports(SnapshotMetadata<K, V>[] imports) {
        this.imports = imports;
    }

    /**
     * Gets the meta-data (location, filter and format) used to read a data snapshot into an entire Cache
     * or individual Region.
     *
     * @return an array of snapshot meta-data used for each import.
     * @see SnapshotServiceFactoryBean.SnapshotMetadata
     */
    protected SnapshotMetadata<K, V>[] getImports() {
        return nullSafeArray(imports);
    }

    /**
     * Sets a reference to the GemFire Region for which the snapshot will be taken.
     *
     * @param region the GemFire Region used to create an instance of the RegionSnapshotService.
     * @see com.gemstone.gemfire.cache.Region
     * @see #getRegion()
     */
    public void setRegion(Region<K, V> region) {
        this.region = region;
    }

    /**
     * Gets a reference to the GemFire Region for which the snapshot will be taken.
     *
     * @return the GemFire Region used to create an instance of the RegionSnapshotService.
     * @see com.gemstone.gemfire.cache.Region
     * @see #getRegion()
     */
    protected Region<K, V> getRegion() {
        return region;
    }

    /**
     * Sets a boolean condition to indicate whether importing on initialization should be suppressed.
     *
     * @param suppressImportOnInit a Boolean value to indicate whether importing on initialization should be suppressed.
     * @see #getSuppressImportOnInit()
     */
    public void setSuppressImportOnInit(Boolean suppressImportOnInit) {
        this.suppressImportOnInit = suppressImportOnInit;
    }

    /**
     * Determines whether importing on initialization should be suppressed.
     *
     * @return a boolean value indicating whether import on initialization should be suppressed.
     * @see #setSuppressImportOnInit(Boolean)
     * @see #afterPropertiesSet()
     */
    protected boolean getSuppressImportOnInit() {
        return Boolean.TRUE.equals(suppressImportOnInit);
    }

    /**
     * Gets the reference to the GemFire Snapshot Service created by this FactoryBean.
     *
     * @return the GemFire Snapshot Service created by this FactoryBean.
     * @throws Exception if the GemFire Snapshot Service failed to be created.
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapter
     */
    @Override
    public SnapshotServiceAdapter<K, V> getObject() throws Exception {
        return snapshotServiceAdapter;
    }

    /**
     * Gets the type of Snapshot Service created by this FactoryBean.
     *
     * @return a Class object representing the type of Snapshot Service created by this FactoryBean.
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapter
     * @see SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter
     * @see SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter
     */
    @Override
    public Class<?> getObjectType() {
        return (snapshotServiceAdapter != null ? snapshotServiceAdapter.getClass() : SnapshotServiceAdapter.class);
    }

    /**
     * Determines this this FactoryBean creates single GemFire Snapshot Service instances.
     *
     * @return true.
     */
    @Override
    public boolean isSingleton() {
        return true;
    }

    /**
     * Constructs and initializes the GemFire Snapshot Service used to take a snapshot of the configured Cache
     * or Region if initialized.  In addition, this initialization method will perform the actual import.
     *
     * @throws Exception if the construction and initialization of the GemFire Snapshot Service fails.
     * @see org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.SnapshotServiceAdapter
     * @see #getSuppressImportOnInit()
     * @see #getImports()
     * @see #create()
     */
    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() throws Exception {
        snapshotServiceAdapter = create();

        if (!getSuppressImportOnInit()) {
            snapshotServiceAdapter.doImport(getImports());
        }
    }

    /**
     * Constructs an appropriate instance of the SnapshotServiceAdapter based on the FactoryBean configuration. If
     * a Region has not been specified, then a GemFire Snapshot Service for the Cache is constructed, otherwise
     * the GemFire Snapshot Service for the configured Region is used.
     *
     * @return a SnapshotServiceAdapter wrapping the appropriate GemFire Snapshot Service (either Cache or Region)
     * depending on the FactoryBean configuration.
     * @see #wrap(CacheSnapshotService)
     * @see #wrap(RegionSnapshotService)
     * @see #getRegion()
     */
    protected SnapshotServiceAdapter create() {
        Region<K, V> region = getRegion();
        return (region != null ? wrap(region.getSnapshotService()) : wrap(getCache().getSnapshotService()));
    }

    /**
     * Wraps the GemFire CacheSnapshotService into an appropriate Adapter to uniformly access snapshot operations
     * on the Cache and Regions alike.
     *
     * @param cacheSnapshotService the GemFire CacheSnapshotService to wrap.
     * @return a SnapshotServiceAdapter wrapping the GemFire CacheSnapshotService.
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapter
     * @see SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter
     * @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService
     */
    protected SnapshotServiceAdapter<Object, Object> wrap(CacheSnapshotService cacheSnapshotService) {
        return new CacheSnapshotServiceAdapter(cacheSnapshotService);
    }

    /**
     * Wraps GemFire's RegionSnapshotService into an appropriate Adapter to uniformly access snapshot operations
     * on the Cache and Regions alike.
     *
     * @param regionSnapshotService the GemFire RegionSnapshotService to wrap.
     * @return a SnapshotServiceAdapter wrapping the GemFire RegionSnapshotService.
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapter
     * @see SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter
     * @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService
     */
    protected SnapshotServiceAdapter<K, V> wrap(RegionSnapshotService<K, V> regionSnapshotService) {
        return new RegionSnapshotServiceAdapter<K, V>(regionSnapshotService);
    }

    /**
     * Performs an export of the GemFire Cache or Region if configured.
     *
     * @throws Exception if the Cache/Region data export operation fails.
     * @see org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.SnapshotServiceAdapter
     * @see #getExports()
     * @see #getObject()
     */
    @Override
    public void destroy() throws Exception {
        getObject().doExport(getExports());
    }

    /**
     * Listens for SnapshotApplicationEvents triggering a GemFire Cache-wide or Region data snapshot import/export
     * when details of the event match the criteria of this factory's constructed GemFire SnapshotService.
     *
     * @param event the SnapshotApplicationEvent triggering a GemFire Cache or Region data import/export.
     * @see org.springframework.data.gemfire.snapshot.SnapshotServiceFactoryBean.SnapshotServiceAdapter
     * @see org.springframework.data.gemfire.snapshot.event.ExportSnapshotApplicationEvent
     * @see org.springframework.data.gemfire.snapshot.event.ImportSnapshotApplicationEvent
     * @see org.springframework.data.gemfire.snapshot.event.SnapshotApplicationEvent
     * @see #isMatch(SnapshotApplicationEvent)
     * @see #resolveSnapshotMetadata(SnapshotApplicationEvent)
     * @see #getObject()
     */
    @Override
    public void onApplicationEvent(SnapshotApplicationEvent<K, V> event) {
        try {
            if (isMatch(event)) {
                if (event instanceof ExportSnapshotApplicationEvent) {
                    getObject().doExport(resolveSnapshotMetadata(event));
                } else {
                    getObject().doImport(resolveSnapshotMetadata(event));
                }
            }
        } catch (Exception ignore) {
        }
    }

    /**
     * Determines whether the details of the given SnapshotApplicationEvent match the criteria of this factory
     * to trigger a GemFire Cache or Region data export.
     *
     * @param event the SnapshotApplicationEvent containing details of the application requested data export.
     * @return a boolean value indicating whether the application requested snapshot event details match
     * the criteria required by this factory to trigger a GemFire Cache or Region data export.
     * @see SnapshotApplicationEvent
     */
    protected boolean isMatch(SnapshotApplicationEvent event) {
        return (event.isCacheSnapshotEvent() || event.matches(getRegion()));
    }

    /**
     * Resolves the SnapshotMetadata used to perform the GemFire Cache or Region data snapshot import/export.
     * If the event contains specific SnapshotMetadata, then this is preferred over the factory's own
     * "import" or "export" SnapshotMetadata.
     *
     * @param event the SnapshotApplicationEvent from which to resolve the SnapshotMetadata.
     * @return the resolved SnapshotMetadata, either from the event or this factory's configured imports/exports.
     * @see SnapshotApplicationEvent#getSnapshotMetadata()
     * @see #getExports()
     * @see #getImports()
     */
    protected SnapshotMetadata<K, V>[] resolveSnapshotMetadata(SnapshotApplicationEvent<K, V> event) {
        SnapshotMetadata<K, V>[] eventSnapshotMetadata = event.getSnapshotMetadata();

        return (!ObjectUtils.isEmpty(eventSnapshotMetadata) ? eventSnapshotMetadata
                : (event instanceof ExportSnapshotApplicationEvent ? getExports() : getImports()));
    }

    /**
     * The SnapshotServiceAdapter interface is an Adapter adapting both GemFire CacheSnapshotService
     * and RegionSnapshotService to treat them uniformly.
     *
     * @param <K> the class type of the Region key.
     * @param <V> the class type of the Region value.
     */
    public interface SnapshotServiceAdapter<K, V> {

        SnapshotOptions<K, V> createOptions();

        void doExport(SnapshotMetadata<K, V>... configurations);

        void doImport(SnapshotMetadata<K, V>... configurations);

        void load(File directory, SnapshotFormat format);

        void load(SnapshotFormat format, SnapshotOptions<K, V> options, File... snapshots);

        void save(File location, SnapshotFormat format);

        void save(File location, SnapshotFormat format, SnapshotOptions<K, V> options);

    }

    /**
     * SnapshotServiceAdapterSupport is an abstract base class for all SnapshotServiceAdapter implementations
     * encapsulating common reusable functionality.
     *
     * @param <K> the class type of the Cache Region key.
     * @param <V> the class type of the Cache Region value.
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapter
     */
    protected static abstract class SnapshotServiceAdapterSupport<K, V> implements SnapshotServiceAdapter<K, V> {

        protected static final File TEMPORARY_DIRECTORY = new File(System.getProperty("java.io.tmpdir"));

        protected final Log log = createLog();

        Log createLog() {
            return LogFactory.getLog(getClass());
        }

        @Override
        public SnapshotOptions<K, V> createOptions() {
            throw new UnsupportedOperationException("not implemented");
        }

        protected SnapshotOptions<K, V> createOptions(SnapshotFilter<K, V> filter) {
            return createOptions().setFilter(filter);
        }

        @Override
        public void doExport(SnapshotMetadata<K, V>... configurations) {
            for (SnapshotMetadata<K, V> configuration : nullSafeArray(configurations)) {
                save(configuration.getLocation(), configuration.getFormat(),
                        createOptions(configuration.getFilter()));
            }
        }

        @Override
        public void doImport(SnapshotMetadata<K, V>... configurations) {
            for (SnapshotMetadata<K, V> configuration : nullSafeArray(configurations)) {
                load(configuration.getFormat(), createOptions(configuration.getFilter()),
                        handleLocation(configuration));
            }
        }

        protected abstract File[] handleLocation(SnapshotMetadata<K, V> configuration);

        protected File[] handleDirectoryLocation(File directory) {
            return directory.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return nullSafeIsFile(pathname);
                }
            });
        }

        protected File[] handleFileLocation(File file) {
            if (ArchiveFileFilter.INSTANCE.accept(file)) {
                try {
                    File extractedArchiveDirectory = new File(TEMPORARY_DIRECTORY,
                            file.getName().replaceAll("\\.", "-"));

                    Assert.state(extractedArchiveDirectory.isDirectory() || extractedArchiveDirectory.mkdirs(),
                            String.format("Failed create directory (%1$s) in which to extract archive (%2$s)",
                                    extractedArchiveDirectory, file));

                    ZipFile zipFile = (ArchiveFileFilter.INSTANCE.isJarFile(file)
                            ? new JarFile(file, false, JarFile.OPEN_READ)
                            : new ZipFile(file, ZipFile.OPEN_READ));

                    for (ZipEntry entry : CollectionUtils.iterable(zipFile.entries())) {
                        if (!entry.isDirectory()) {
                            DataInputStream entryInputStream = new DataInputStream(zipFile.getInputStream(entry));

                            DataOutputStream entryOutputStream = new DataOutputStream(new FileOutputStream(
                                    new File(extractedArchiveDirectory, toSimpleFilename(entry.getName()))));

                            try {
                                FileCopyUtils.copy(entryInputStream, entryOutputStream);
                            } finally {
                                exceptionSuppressingClose(entryInputStream);
                                exceptionSuppressingClose(entryOutputStream);
                            }
                        }
                    }

                    return handleDirectoryLocation(extractedArchiveDirectory);
                } catch (Throwable t) {
                    throw new ImportSnapshotException(
                            String.format("Failed to extract archive (%1$s) to import", file), t);
                }
            }

            return new File[] { file };
        }

        protected boolean exceptionSuppressingClose(Closeable closeable) {
            try {
                closeable.close();
                return true;
            } catch (IOException ignore) {
                logDebug(ignore, "Failed to close (%1$s)", closeable);
                return false;
            }
        }

        protected void logDebug(Throwable t, String message, Object... arguments) {
            if (log.isDebugEnabled()) {
                log.debug(String.format(message, arguments), t);
            }
        }

        @Override
        public void load(File directory, SnapshotFormat format) {
            throw new UnsupportedOperationException("not implemented");
        }

        @Override
        public void load(SnapshotFormat format, SnapshotOptions<K, V> options, File... snapshots) {
            throw new UnsupportedOperationException("not implemented");
        }

        @Override
        public void save(File location, SnapshotFormat format) {
            throw new UnsupportedOperationException("not implemented");
        }

        @Override
        public void save(File location, SnapshotFormat format, SnapshotOptions<K, V> options) {
            throw new UnsupportedOperationException("not implemented");
        }

        protected String toSimpleFilename(String pathname) {
            int pathSeparatorIndex = String.valueOf(pathname).lastIndexOf(File.separator);
            pathname = (pathSeparatorIndex > -1 ? pathname.substring(pathSeparatorIndex + 1) : pathname);
            return StringUtils.trimWhitespace(pathname);
        }
    }

    /**
     * The CacheSnapshotServiceAdapter is a SnapshotServiceAdapter adapting GemFire's CacheSnapshotService.
     *
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapterSupport
     */
    protected static class CacheSnapshotServiceAdapter extends SnapshotServiceAdapterSupport<Object, Object> {

        private final CacheSnapshotService snapshotService;

        public CacheSnapshotServiceAdapter(CacheSnapshotService snapshotService) {
            Assert.notNull(snapshotService, "The backing CacheSnapshotService must not be null");
            this.snapshotService = snapshotService;
        }

        protected CacheSnapshotService getSnapshotService() {
            return snapshotService;
        }

        @Override
        public SnapshotOptions<Object, Object> createOptions() {
            return getSnapshotService().createOptions();
        }

        @Override
        protected File[] handleLocation(SnapshotMetadata<Object, Object> configuration) {
            return (configuration.isFile() ? handleFileLocation(configuration.getLocation())
                    : handleDirectoryLocation(configuration.getLocation()));
        }

        @Override
        public void load(File directory, SnapshotFormat format) {
            try {
                getSnapshotService().load(directory, format);
            } catch (Throwable t) {
                throw new ImportSnapshotException(String.format(
                        "Failed to load snapshots from directory (%1$s) in format (%2$s)", directory, format), t);
            }
        }

        @Override
        public void load(SnapshotFormat format, SnapshotOptions<Object, Object> options, File... snapshots) {
            try {
                getSnapshotService().load(snapshots, format, options);
            } catch (Throwable t) {
                throw new ImportSnapshotException(
                        String.format("Failed to load snapshots (%1$s) in format (%2$s) using options (%3$s)",
                                Arrays.toString(snapshots), format, options),
                        t);
            }
        }

        @Override
        public void save(File directory, SnapshotFormat format) {
            try {
                getSnapshotService().save(directory, format);
            } catch (Throwable t) {
                throw new ExportSnapshotException(String.format(
                        "Failed to save snapshots to directory (%1$s) in format (%2$s)", directory, format), t);
            }
        }

        @Override
        public void save(File directory, SnapshotFormat format, SnapshotOptions<Object, Object> options) {
            try {
                getSnapshotService().save(directory, format, options);
            } catch (Throwable t) {
                throw new ExportSnapshotException(String.format(
                        "Failed to save snapshots to directory (%1$s) in format (%2$s) using options (%3$s)",
                        directory, format, options), t);
            }
        }
    }

    /**
     * The RegionSnapshotServiceAdapter is a SnapshotServiceAdapter adapting GemFire's RegionSnapshotService.
     *
     * @see SnapshotServiceFactoryBean.SnapshotServiceAdapterSupport
     */
    protected static class RegionSnapshotServiceAdapter<K, V> extends SnapshotServiceAdapterSupport<K, V> {

        private final RegionSnapshotService<K, V> snapshotService;

        public RegionSnapshotServiceAdapter(RegionSnapshotService<K, V> snapshotService) {
            Assert.notNull(snapshotService, "The backing RegionSnapshotService must not be null");
            this.snapshotService = snapshotService;
        }

        protected RegionSnapshotService<K, V> getSnapshotService() {
            return snapshotService;
        }

        @Override
        public SnapshotOptions<K, V> createOptions() {
            return getSnapshotService().createOptions();
        }

        @Override
        protected File[] handleLocation(final SnapshotMetadata<K, V> configuration) {
            return new File[] { configuration.getLocation() };
        }

        @Override
        public void load(File snapshot, SnapshotFormat format) {
            try {
                getSnapshotService().load(snapshot, format);
            } catch (Throwable t) {
                throw new ImportSnapshotException(String
                        .format("Failed to load snapshot from file (%1$s) in format (%2$s)", snapshot, format), t);
            }
        }

        @Override
        public void load(SnapshotFormat format, SnapshotOptions<K, V> options, File... snapshots) {
            try {
                for (File snapshot : snapshots) {
                    getSnapshotService().load(snapshot, format, options);
                }
            } catch (Throwable t) {
                throw new ImportSnapshotException(
                        String.format("Failed to load snapshots (%1$s) in format (%2$s) using options (%3$s)",
                                Arrays.toString(snapshots), format, options),
                        t);
            }
        }

        @Override
        public void save(File snapshot, SnapshotFormat format) {
            try {
                getSnapshotService().save(snapshot, format);
            } catch (Throwable t) {
                throw new ExportSnapshotException(
                        String.format("Failed to save snapshot to file (%1$s) in format (%2$s)", snapshot, format),
                        t);
            }
        }

        @Override
        public void save(File snapshot, SnapshotFormat format, SnapshotOptions<K, V> options) {
            try {
                getSnapshotService().save(snapshot, format, options);
            } catch (Throwable t) {
                throw new ExportSnapshotException(String.format(
                        "Failed to save snapshot to file (%1$s) in format (%2$s) using options (%3$s)", snapshot,
                        format, options), t);
            }
        }
    }

    /**
     * The SnapshotMetadata class encapsulates details of the GemFire Cache or Region data snapshot
     * on either import or export.
     *
     * @param <K> the class type of the Region key.
     * @param <V> the class type of the Region value.
     */
    public static class SnapshotMetadata<K, V> {

        private final File location;

        private final SnapshotFilter<K, V> filter;

        private final SnapshotFormat format;

        public SnapshotMetadata(File location, SnapshotFormat format) {
            this(location, null, format);
        }

        public SnapshotMetadata(File location, SnapshotFilter<K, V> filter, SnapshotFormat format) {
            Assert.notNull(location, "Location must not be null");

            this.location = location;
            this.filter = filter;
            this.format = format;
        }

        public boolean isDirectory() {
            return nullSafeIsDirectory(getLocation());
        }

        public boolean isFile() {
            return nullSafeIsFile(getLocation());
        }

        public File getLocation() {
            return location;
        }

        public boolean isFilterPresent() {
            return (getFilter() != null);
        }

        public SnapshotFilter<K, V> getFilter() {
            return filter;
        }

        public SnapshotFormat getFormat() {
            return (format != null ? format : SnapshotFormat.GEMFIRE);
        }

        @Override
        public String toString() {
            return String.format("{ @type = %1$s, location = %2$s, filter = %2$s, format = %4$s }",
                    getClass().getName(), getLocation().getAbsolutePath(), getFilter(), getFormat());
        }
    }

    /**
     * The ArchiveFileFilter class is a Java FileFilter implementation accepting any File that is either
     * a JAR file or ZIP file.
     *
     * @see java.io.File
     * @see java.io.FileFilter
     */
    protected static final class ArchiveFileFilter implements FileFilter {

        protected static final ArchiveFileFilter INSTANCE = new ArchiveFileFilter();

        protected static final List<String> ACCEPTED_FILE_EXTENSIONS = Arrays.asList("jar", "zip");

        protected static final String FILE_EXTENSION_DOT_SEPARATOR = ".";

        protected boolean isJarFile(File file) {
            return "jar".equalsIgnoreCase(getFileExtension(file));
        }

        protected String getFileExtension(File file) {
            String fileExtension = "";

            if (nullSafeIsFile(file)) {
                String pathname = file.getAbsolutePath();
                int fileExtensionIndex = pathname.lastIndexOf(FILE_EXTENSION_DOT_SEPARATOR);
                fileExtension = (fileExtensionIndex > -1 ? pathname.substring(fileExtensionIndex + 1) : "");
            }

            return fileExtension.toLowerCase();
        }

        @Override
        public boolean accept(final File pathname) {
            return ACCEPTED_FILE_EXTENSIONS.contains(getFileExtension(pathname));
        }
    }

}