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.io.File;
021import java.util.Map;
022
023import org.syncany.plugins.transfer.files.DatabaseRemoteFile;
024import org.syncany.plugins.transfer.files.MultichunkRemoteFile;
025import org.syncany.plugins.transfer.files.RemoteFile;
026import org.syncany.plugins.transfer.files.SyncanyRemoteFile;
027
028/**
029 * The transfer manager synchronously connects to the remote storage. It is
030 * responsible for file upload, download and deletion.
031 *
032 * <p>All its operations are strictly <b>synchronous</b> and throw a
033 * {@code StorageException} if they fail. The implementations have to make sure
034 * that
035 * <ul>
036 *   <li>the repository is not corrupted, e.g. duplicate files or corrupt files
037 *   <li>files matching the specified file format are complete, i.e. fully uploaded
038 *   <li>methods that need an established connections re-connect if necessary
039 * </ul>
040 *
041 * <p>A transfer manager may organize files according to their type or name as
042 * it is optimal for the given storage. {@link RemoteFile}s can be classified
043 * by their sub-type. For network-transfer optimization reasons, it might be
044 * useful to place {@link MultichunkRemoteFile}s and {@link DatabaseRemoteFile}s
045 * in a separate sub-folder on the remote storage.
046 *
047 * @author Philipp C. Heckel (philipp.heckel@gmail.com)
048 */
049public interface TransferManager {
050        /**
051         * Establish a connection with the remote storage.
052         *
053         * <p>This method does not validate the correctness of the repository and
054         * it does not create any folders. The former is done by {@link #test(boolean)}, the
055         * latter is done by {@link #init(boolean)}.
056         *
057         * @throws StorageException If the connection fails due to no Internet connection,
058         *         authentication errors, etc.
059         */
060        public void connect() throws StorageException;
061
062        /**
063         * Disconnect from the remote storage.
064         *
065         * @throws StorageException If the connection fails due to no Internet connection,
066         *         authentication errors, etc.
067         */
068        public void disconnect() throws StorageException;
069
070        /**
071         * Initialize remote storage. This method is called to set up a new repository.
072         *
073         * @param  createIfRequired true if the method should handle repo creation
074         * @throws StorageException If the connection drops, or any other
075         *         exception occurs.
076         */
077        public void init(boolean createIfRequired) throws StorageException;
078
079        /**
080         * Download an existing remote file to the local disk.
081         *
082         * <p>The file is either downloaded completely or nothing at all. In the latter
083         * case, a {@code StorageException} is thrown.
084         *
085         * <p>Implementations must make sure that if a file matches the specified name
086         * schema, it must be complete and consistent.
087         *
088         * <p>If remoteFile does not exist, a {@link StorageFileNotFoundException} is thrown.
089         *
090         * @param remoteFile Existing source file on the remote storage.
091         *        The only required property of the remote file is the name.
092         * @param localFile Not existing local file to which the remote file is
093         *        going to be downloaded.
094         * @throws StorageException If the connection fails due to no Internet connection,
095         *         authentication errors, etc.
096         */
097        public void download(RemoteFile remoteFile, File localFile) throws StorageException;
098
099        /**
100         * Update an existing local file to the online storage.
101         *
102         * <p>The file is either uploaded completely or nothing at all. In the latter
103         * case, a {@code StorageException} is thrown.
104         *
105         * <p>Implementations must make sure that if a file matches the specified name
106         * schema, it must be complete and consistent.
107         *
108         * <p>Implementations must NOT upload a file if it already exists and has
109         * the same file size as the local file.
110         *
111         * @param localFile Existing local file that is going to be uploaded.
112         * @param remoteFile Not existing destination file on the remote storage.
113         *        The only required property of the remote file is the name.
114         * @throws StorageException If the connection fails due to no Internet connection,
115         *         authentication errors, etc.
116         */
117        public void upload(File localFile, RemoteFile remoteFile) throws StorageException;
118
119        /**
120         * Moves an existing file in the online storage.
121         *
122         * <p> If the sourceFile does not exists, a {@link StorageMoveException} is thrown.
123         *
124         * @param sourceFile Existing remote file that is to be moved.
125         * @param targetFile Destination for the remote file.
126         * @throws StorageException If the connection fails due to no Internet connection,
127         *         authentication errors, etc.
128         */
129        public void move(RemoteFile sourceFile, RemoteFile targetFile) throws StorageException;
130
131        /**
132         * Deletes an existing file from the remote storage permanently.
133         *
134         * <p>In case the remote file does not exist, it returns immediately without
135         * any notice. If the file cannot be deleted or the connection breaks,
136         * a {@code StorageException} is thrown.
137         *
138         * @param remoteFile Existing remote file to be deleted.
139         *        The only required property of the remote file is the name.
140         * @throws StorageException If the connection fails due to no Internet connection,
141         *         authentication errors, etc
142         */
143        public boolean delete(RemoteFile remoteFile) throws StorageException;
144
145        /**
146         * Retrieves a list of all files in the remote repository, filtered by
147         * the type of the desired file, i.e. by a sub-class of {@link RemoteFile}.
148         *
149         * @param remoteFileClass Filter class: <code>RemoteFile</code> or a sub-type thereof
150         * @return Returns a list of remote files. In the map, the key is the file name,
151         *         the value the entire {@link RemoteFile} object.
152         * @throws StorageException If the connection fails due to no Internet connection,
153         *         authentication errors, etc
154         */
155        public <T extends RemoteFile> Map<String, T> list(Class<T> remoteFileClass) throws StorageException;
156
157        /**
158         * Tests whether the repository parameters are valid. In particular, the method tests
159         * whether a target (folder, bucket, etc.) exists or, if not, whether it can be created.
160         * It furthermore tests whether a repository at the target already exists by checking if the
161         * {@link SyncanyRemoteFile} exists.
162         *
163         * <p>The relevant result is determined by the following methods:
164         *
165         * <ul>
166         *  <li>{@link #testTargetExists()}: Tests whether the target exists.</li>
167         *  <li>{@link #testTargetCanWrite()}: Tests whether the target is writable.</li>
168         *  <li>{@link #testTargetCanCreate()}: Tests whether the target can be created if it does not
169         *      exist already. This is only called if <code>testCreateTarget</code> is set.</li>
170         *  <li>{@link #testRepoFileExists()}: Tests whether the repo file exists.</li>
171         * </ul>
172         *
173         * @return Returns the result of testing the repository.
174         * @param testCreateTarget If <code>true</code>, the test will test if the target can be created in case
175         *        it does not exist. If <code>false</code>, this test will be skipped.
176         * @see StorageTestResult
177         */
178        public StorageTestResult test(boolean testCreateTarget);
179
180        /**
181         * Tests whether the target path/folder <b>exists</b>. This might be done by listing the parent path/folder
182         * or by retrieving metadata about the target. The method returns <code>true</code> if the target exists,
183         * <code>false</code> otherwise.
184         *
185         * <p>This method is called by the {@link #test(boolean)} method (only during repository initialization
186         * or initial connection).
187         *
188         * @return Returns <code>true</code> if the target exists, <code>false</code> otherwise
189         * @throws StorageException If the test cannot be performed, e.g. due to a connection failure
190         */
191        public boolean testTargetExists() throws StorageException;
192
193        /**
194         * Tests whether the target path/folder is <b>writable</b> by the application. This method may either
195         * check the write permissions of the target or actually write a test file to check write access. If the
196         * target does not exist, <code>false</code> is returned. If the target exists and is writable, <code>true</code>
197         * is returned.
198         *
199         * <p>This method is called by the {@link #test(boolean)} method (only during repository initialization
200         * or initial connection).
201         *
202         * @return Returns <code>true</code> if the target can be written to, <code>false</code> otherwise
203         * @throws StorageException If the test cannot be performed, e.g. due to a connection failure
204         */
205        public boolean testTargetCanWrite() throws StorageException;
206
207        /**
208         * Tests whether the target path/folder <b>can be created</b> (if it <b>does not exist already</b>). This method
209         * may either check the permissions of the parent path/folder or actually create and delete the target to
210         * determine create permissions.
211         *
212         * <p>If the target already exists, the method returns <code>true</code>. If it does not, but it can be created
213         * (according to tests of this method), it also returns <code>true</code>. In all other cases, <code>false</code> is returned.
214         *
215         * <p>This method is called by the {@link #test(boolean)} method, <b>but only if</b> the <code>testCreateTarget</code> flag
216         * is set to <code>true</code>!
217         *
218         * @return Returns <code>true</code> if the target can be created or already exists, <code>false</code> otherwise
219         * @throws StorageException If the test cannot be performed, e.g. due to a connection failure
220         */
221        public boolean testTargetCanCreate() throws StorageException;
222
223        /**
224         * Tests whether the <b>repository file exists</b> (see {@link SyncanyRemoteFile}). This method is called by the {@link #test(boolean)} method
225         * (only during repository initialization (or initial connection).
226         *
227         * <p>This method is called by the {@link #test(boolean)} method (only during repository initialization
228         * or initial connection).
229         *
230         * @return Returns <code>true</code> if the repository is valid, <code>false</code> otherwise
231         * @throws StorageException If the test cannot be performed, e.g. due to a connection failure
232         */
233        public boolean testRepoFileExists() throws StorageException;
234
235        /**
236         * Return the path for a concrete {@link org.syncany.plugins.transfer.files.RemoteFile} implementation as it is stored on the
237         * remote side
238         * *
239         * @param remoteFileClass The class to provide the path for
240         * @return A string pointing to the folder where a file is stored
241         */
242        public String getRemoteFilePath(Class<? extends RemoteFile> remoteFileClass);
243        
244}