net.solarnetwork.node.backup.DefaultBackupManager.java Source code

Java tutorial

Introduction

Here is the source code for net.solarnetwork.node.backup.DefaultBackupManager.java

Source

/* ==================================================================
 * DefaultBackupManager.java - Mar 27, 2013 9:17:24 AM
 * 
 * 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.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import net.solarnetwork.node.settings.SettingSpecifier;
import net.solarnetwork.node.settings.support.BasicRadioGroupSettingSpecifier;
import net.solarnetwork.node.util.PrefixedMessageSource;
import net.solarnetwork.util.OptionalService;
import net.solarnetwork.util.UnionIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.HierarchicalMessageSource;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.FileCopyUtils;

/**
 * Default implementation of {@link BackupManager}.
 * 
 * <p>
 * The configurable properties of this class are:
 * </p>
 * 
 * <dl class="class-properties">
 * <dt>backupServiceTracker</dt>
 * <dd>A tracker for the desired backup service to use.</dd>
 * 
 * <dt>resourceProviders</dt>
 * <dd>The collection of {@link BackupResourceProvider} instances that provide
 * the resources to be backed up.</dd>
 * </dl>
 * 
 * @author matt
 * @version 1.0
 */
public class DefaultBackupManager implements BackupManager {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private Collection<BackupService> backupServices;
    private OptionalService<BackupService> backupServiceTracker;
    private Collection<BackupResourceProvider> resourceProviders;
    private ExecutorService executorService = defaultExecutorService();

    private static HierarchicalMessageSource getMessageSourceInstance() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBundleClassLoader(DefaultBackupManager.class.getClassLoader());
        source.setBasename(DefaultBackupManager.class.getName());
        return source;
    }

    private static ExecutorService defaultExecutorService() {
        // we want at most one backup happening at a time by default
        return new ThreadPoolExecutor(0, 1, 5, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(3, true));
    }

    @Override
    public String getSettingUID() {
        return getClass().getName();
    }

    @Override
    public String getDisplayName() {
        return "Backup Manager";
    }

    @Override
    public MessageSource getMessageSource() {
        HierarchicalMessageSource source = getMessageSourceInstance();
        HierarchicalMessageSource child = source;
        for (BackupService backupService : backupServices) {
            PrefixedMessageSource ps = new PrefixedMessageSource();
            ps.setDelegate(backupService.getSettingSpecifierProvider().getMessageSource());
            ps.setPrefix(backupService.getKey() + ".");
            child.setParentMessageSource(ps);
            child = ps;
        }

        return source;
    }

    @Override
    public List<SettingSpecifier> getSettingSpecifiers() {
        List<SettingSpecifier> results = new ArrayList<SettingSpecifier>(20);
        BasicRadioGroupSettingSpecifier serviceSpec = new BasicRadioGroupSettingSpecifier(
                "backupServiceTracker.propertyFilters['key']", FileSystemBackupService.KEY);
        Map<String, String> serviceSpecValues = new TreeMap<String, String>();
        for (BackupService service : backupServices) {
            serviceSpecValues.put(service.getKey(), service.getSettingSpecifierProvider().getDisplayName());
        }
        serviceSpec.setValueTitles(serviceSpecValues);
        results.add(serviceSpec);
        return results;
    }

    @Override
    public BackupService activeBackupService() {
        return backupServiceTracker.service();
    }

    @Override
    public Iterable<BackupResource> resourcesForBackup() {
        BackupService service = activeBackupService();
        if (service == null) {
            log.debug("No BackupService available, can't find resources for backup");
            return Collections.emptyList();
        }
        if (service.getInfo().getStatus() != BackupStatus.Configured) {
            log.info("BackupService {} in {} state, can't find resources for backup", service.getKey(),
                    service.getInfo().getStatus());
            return Collections.emptyList();
        }

        final List<Iterator<BackupResource>> resources = new ArrayList<Iterator<BackupResource>>(10);
        for (BackupResourceProvider provider : resourceProviders) {
            // map each resource into a sub directory
            Iterator<BackupResource> itr = provider.getBackupResources().iterator();
            resources.add(new PrefixedBackupResourceIterator(itr, provider.getKey()));

        }
        return new Iterable<BackupResource>() {

            @Override
            public Iterator<BackupResource> iterator() {
                return new UnionIterator<BackupResource>(resources);
            }

        };
    }

    @Override
    public Backup createBackup() {
        final BackupService service = activeBackupService();
        if (service == null) {
            log.debug("No active backup service available, cannot perform backup");
            return null;
        }
        final BackupServiceInfo info = service.getInfo();
        final BackupStatus status = info.getStatus();
        if (status != BackupStatus.Configured) {
            log.info("BackupService {} is in the {} state; cannot perform backup", service.getKey(), status);
            return null;
        }

        log.info("Initiating backup to service {}", service.getKey());
        final Backup backup = service.performBackup(resourcesForBackup());
        if (backup != null) {
            log.info("Backup {} {} with service {}", backup.getKey(),
                    (backup.isComplete() ? "completed" : "initiated"), service.getKey());
        }
        return backup;
    }

    @Override
    public Future<Backup> createAsynchronousBackup() {
        assert executorService != null;
        return executorService.submit(new Callable<Backup>() {

            @Override
            public Backup call() throws Exception {
                return createBackup();
            }

        });
    }

    @Override
    public void exportBackupArchive(String backupKey, OutputStream out) throws IOException {
        final BackupService service = activeBackupService();
        if (service == null) {
            return;
        }

        final Backup backup = service.backupForKey(backupKey);
        if (backup == null) {
            return;
        }

        // create the zip archive for the backup files
        ZipOutputStream zos = new ZipOutputStream(out);
        try {
            BackupResourceIterable resources = service.getBackupResources(backup);
            for (BackupResource r : resources) {
                zos.putNextEntry(new ZipEntry(r.getBackupPath()));
                FileCopyUtils.copy(r.getInputStream(), new FilterOutputStream(zos) {

                    @Override
                    public void close() throws IOException {
                        // FileCopyUtils closed the stream, which we don't want here
                    }

                });
            }
            resources.close();
        } finally {
            zos.flush();
            zos.finish();
            zos.close();
        }
    }

    @Override
    public void importBackupArchive(InputStream archive) throws IOException {
        final ZipInputStream zin = new ZipInputStream(archive);
        while (true) {
            final ZipEntry entry = zin.getNextEntry();
            if (entry == null) {
                break;
            }
            final String path = entry.getName();
            log.debug("Restoring backup resource {}", path);
            final int providerIndex = path.indexOf('/');
            if (providerIndex != -1) {
                final String providerKey = path.substring(0, providerIndex);
                for (BackupResourceProvider provider : resourceProviders) {
                    if (providerKey.equals(provider.getKey())) {
                        provider.restoreBackupResource(new BackupResource() {

                            @Override
                            public String getBackupPath() {
                                return path.substring(providerIndex + 1);
                            }

                            @Override
                            public InputStream getInputStream() throws IOException {
                                return new FilterInputStream(zin) {

                                    @Override
                                    public void close() throws IOException {
                                        // don't close me
                                    }

                                };
                            }

                            @Override
                            public long getModificationDate() {
                                return entry.getTime();
                            }

                        });
                        break;
                    }
                }
            }
        }
    }

    @Override
    public void restoreBackup(Backup backup) {
        BackupService service = backupServiceTracker.service();
        if (service == null) {
            return;
        }
        BackupResourceIterable resources = service.getBackupResources(backup);
        try {
            for (final BackupResource r : resources) {
                // top-level dir is the  key of the provider
                final String path = r.getBackupPath();
                log.debug("Restoring backup {} resource {}", backup.getKey(), path);
                final int providerIndex = path.indexOf('/');
                if (providerIndex != -1) {
                    final String providerKey = path.substring(0, providerIndex);
                    for (BackupResourceProvider provider : resourceProviders) {
                        if (providerKey.equals(provider.getKey())) {
                            provider.restoreBackupResource(new BackupResource() {

                                @Override
                                public String getBackupPath() {
                                    return path.substring(providerIndex + 1);
                                }

                                @Override
                                public InputStream getInputStream() throws IOException {
                                    return r.getInputStream();
                                }

                                @Override
                                public long getModificationDate() {
                                    return r.getModificationDate();
                                }

                            });
                            break;
                        }
                    }
                }
            }
        } finally {
            try {
                resources.close();
            } catch (IOException e) {
                // ignore
            }
        }
    }

    private static class PrefixedBackupResourceIterator implements Iterator<BackupResource> {

        private final Iterator<BackupResource> delegate;
        private final String prefix;

        private PrefixedBackupResourceIterator(Iterator<BackupResource> delegate, String prefix) {
            super();
            this.delegate = delegate;
            this.prefix = prefix;
        }

        @Override
        public boolean hasNext() {
            return delegate.hasNext();
        }

        @Override
        public BackupResource next() {
            final BackupResource r = delegate.next();
            return new BackupResource() {

                @Override
                public String getBackupPath() {
                    return prefix + '/' + r.getBackupPath();
                }

                @Override
                public InputStream getInputStream() throws IOException {
                    return r.getInputStream();
                }

                @Override
                public long getModificationDate() {
                    return r.getModificationDate();
                }

            };
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    public void setBackupServiceTracker(OptionalService<BackupService> backupServiceTracker) {
        this.backupServiceTracker = backupServiceTracker;
    }

    public void setBackupServices(Collection<BackupService> backupServices) {
        this.backupServices = backupServices;
    }

    public void setResourceProviders(Collection<BackupResourceProvider> resourceProviders) {
        this.resourceProviders = resourceProviders;
    }

    public void setExecutorService(ExecutorService executorService) {
        this.executorService = executorService;
    }

}