001/*
002 * Syncany, www.syncany.org
003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com>
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package org.syncany.plugins.transfer;
019
020import java.lang.reflect.Constructor;
021import java.lang.reflect.InvocationTargetException;
022
023import org.syncany.config.Config;
024import org.syncany.plugins.Plugin;
025import org.syncany.plugins.transfer.files.RemoteFile;
026import org.syncany.util.ReflectionUtil;
027
028/**
029 * The transfer plugin is a special plugin responsible for transferring files
030 * to the remote storage. Implementations must provide implementations for
031 * {@link TransferPlugin} (this class), {@link TransferSettings} (connection
032 * details) and {@link TransferManager} (transfer methods).<br><br>
033 *
034 * <p>Plugins have to follow a naming convention:
035 * <ul>
036 *   <li>Package names have to be lower snaked cased</li>
037 *   <li>Class names have to be camel cased</li>
038 *   <li>Package names will be converted to class names by replacing underscores ('_') and uppercasing the
039 *      subsequent character.</li>
040 * </ul>
041 *
042 * <p>Example:</p>
043 * A plugin is called DummyPlugin, hence <i>org.syncany.plugins.dummy_plugin.DummyPluginTransferPlugin</i> is the
044 * plugin's {@link TransferPlugin} class and <i>org.syncany.plugins.dummy_plugin.DummyPluginTransferSettings</i> is the
045 * corresponding {@link TransferSettings} implementation.
046 *
047 * @author Philipp C. Heckel (philipp.heckel@gmail.com)
048 * @author Christian Roth (christian.roth@port17.de)
049 */
050public abstract class TransferPlugin extends Plugin {
051        public TransferPlugin(String pluginId) {
052                super(pluginId);
053        }
054
055        /**
056         * Creates an empty plugin-specific {@link org.syncany.plugins.transfer.TransferSettings} instance.
057         *
058         * @return Empty plugin-specific {@link org.syncany.plugins.transfer.TransferSettings} instance.
059         * @throws StorageException Thrown if no {@link org.syncany.plugins.transfer.TransferSettings} are attached to a
060         *         plugin
061         */
062        @SuppressWarnings("unchecked")
063        public final <T extends TransferSettings> T createEmptySettings() throws StorageException {
064                final Class<? extends TransferSettings> transferSettings = TransferPluginUtil.getTransferSettingsClass(this.getClass());
065
066                if (transferSettings == null) {
067                        throw new StorageException("TransferPlugin does not have any settings attached!");
068                }
069
070                try {
071                        return (T) transferSettings.newInstance();
072                }
073                catch (InstantiationException | IllegalAccessException e) {
074                        throw new RuntimeException("Unable to create TransferSettings: " + e.getMessage());
075                }
076        }
077
078         /**
079         * Creates an initialized, plugin-specific {@link org.syncany.plugins.transfer.TransferManager} object using the given
080         * connection details.
081         *
082         * <p>The created instance can be used to upload/download/delete {@link RemoteFile}s
083         * and query the remote storage for a file list.
084         *
085         * @param transferSettings A valid {@link org.syncany.plugins.transfer.TransferSettings} instance.
086         * @param config A valid {@link org.syncany.config.Config} instance.
087         * @return A initialized, plugin-specific {@link org.syncany.plugins.transfer.TransferManager} instance.
088         * @throws StorageException Thrown if no (valid) {@link org.syncany.plugins.transfer.TransferManager} are attached to
089        *  a plugin
090         */
091        @SuppressWarnings("unchecked")
092        public final <T extends TransferManager> T createTransferManager(TransferSettings transferSettings, Config config) throws StorageException {
093                if (!transferSettings.isValid()) {
094                        throw new StorageException("Unable to create transfer manager: connection isn't valid (perhaps missing some mandatory fields?)");
095                }
096
097                final Class<? extends TransferSettings> transferSettingsClass = TransferPluginUtil.getTransferSettingsClass(this.getClass());
098                final Class<? extends TransferManager> transferManagerClass = TransferPluginUtil.getTransferManagerClass(this.getClass());
099
100                if (transferSettingsClass == null) {
101                        throw new RuntimeException("Unable to create transfer manager: No settings class attached");
102                }
103
104                if (transferManagerClass == null) {
105                        throw new RuntimeException("Unable to create transfer manager: No manager class attached");
106                }
107
108                try {
109                        Constructor<?> potentialConstructor = ReflectionUtil.getMatchingConstructorForClass(transferManagerClass, TransferSettings.class,
110                                        Config.class);
111
112                        if (potentialConstructor == null) {
113                                throw new RuntimeException("Invalid arguments for constructor in pluginclass -- must be 2 and subclass of " + TransferSettings.class
114                                                + " and " + Config.class);
115                        }
116
117                        return (T) potentialConstructor.newInstance(transferSettingsClass.cast(transferSettings), config);
118                }
119                catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
120                        throw new RuntimeException("Unable to create transfer settings: " + e.getMessage(), e);
121                }
122        }
123}