001package co.codewizards.cloudstore.core.repo.local;
002
003import java.lang.reflect.InvocationHandler;
004import java.net.URL;
005import java.util.Map;
006import java.util.UUID;
007import java.util.concurrent.locks.Lock;
008
009import co.codewizards.cloudstore.core.appid.AppIdRegistry;
010import co.codewizards.cloudstore.core.oio.File;
011import co.codewizards.cloudstore.core.progress.ProgressMonitor;
012
013public interface LocalRepoManager extends AutoCloseable {
014        String APP_ID_SIMPLE_ID = AppIdRegistry.getInstance().getAppIdOrFail().getSimpleId();
015
016        String SYSTEM_PROPERTY_KEY_SIZE = APP_ID_SIMPLE_ID + ".repository.asymmetricKey.size";
017        int DEFAULT_KEY_SIZE = 4096;
018
019        /**
020         * @deprecated replaced by {@link #CONFIG_KEY_CLOSE_DEFERRED_MILLIS}.
021         */
022        @Deprecated
023        String SYSTEM_PROPERTY_CLOSE_DEFERRED_MILLIS = APP_ID_SIMPLE_ID + ".localRepoManager.closeDeferredMillis";
024        String CONFIG_KEY_CLOSE_DEFERRED_MILLIS = "localRepoManager.closeDeferredMillis";
025        long DEFAULT_CLOSE_DEFERRED_MILLIS = 20L * 1000L;
026
027        String META_DIR_NAME = "." + APP_ID_SIMPLE_ID + "-repo";
028        /**
029         * Temporary directory potentially inside every directory.
030         * <p>
031         * For example: <code>${repo}/Accounting/2019/.cloudstore-tmp/</code>
032         */
033        String TEMP_DIR_NAME = "." + APP_ID_SIMPLE_ID + "-tmp";
034        String TEMP_NEW_FILE_PREFIX = "." + APP_ID_SIMPLE_ID + "-new_";
035
036        /**
037         * Repository-wide global directory inside the meta-directory.
038         * <p>
039         * For example: <code>${repo}/.cloudstore-repo/tmp/</code>
040         */
041        String REPO_TEMP_DIR_NAME = "tmp";
042
043        String REPOSITORY_LOCK_FILE_NAME = APP_ID_SIMPLE_ID + "-repository.lock";
044        String REPOSITORY_PROPERTIES_FILE_NAME = APP_ID_SIMPLE_ID + "-repository.properties";
045        String PROP_REPOSITORY_ID = "repository.id";
046        String PROP_VERSION = "repository.version";
047        /**
048         * Aliases separated by '/' (because '/' is an illegal character for an alias).
049         * <p>
050         * To make scripting easier (e.g. using grep), the aliases start and end with a
051         * '/'. For example: "/alias1/alias2/alias3/"
052         */
053        String PROP_REPOSITORY_ALIASES = "repository.aliases";
054
055        String PERSISTENCE_PROPERTIES_FILE_NAME = APP_ID_SIMPLE_ID + "-persistence.properties";
056
057        String VAR_REPOSITORY_ID = "repository.id";
058        String VAR_LOCAL_ROOT = "repository.localRoot";
059        String VAR_META_DIR = "repository.metaDir";
060
061        /**
062         * Gets the repository's local root directory.
063         * <p>
064         * This file is canonical (absolute and symbolic links resolved).
065         * @return the repository's local root directory. Never <code>null</code>.
066         */
067        File getLocalRoot();
068
069        /**
070         * Gets the local repository's unique ID.
071         * <p>
072         * This is {@link LocalRepository#getEntityID() LocalRepository.entityID} in the local repository database.
073         * @return the local repository's unique ID. Never <code>null</code>.
074         */
075        UUID getRepositoryId();
076
077        /**
078         * Gets the local repository's private key.
079         * <p>
080         * This is always an RSA key - other key types are not (yet) supported.
081         * @return the local repository's private key. Never <code>null</code>.
082         */
083        byte[] getPrivateKey();
084
085        /**
086         * Gets the local repository's public key.
087         * <p>
088         * This is always an RSA key - other key types are not (yet) supported.
089         * @return the local repository's public key. Never <code>null</code>.
090         */
091        byte[] getPublicKey();
092
093        /**
094         * Gets the remote repository's public key.
095         * <p>
096         * This is always an RSA key - other key types are not (yet) supported.
097         * @param repositoryId the remote repository's unique ID. Must not be <code>null</code>.
098         * @return the remote repository's public key. Never <code>null</code>.
099         * @throws IllegalArgumentException if there is no remote-repository with the given {@code repositoryId}.
100         */
101        byte[] getRemoteRepositoryPublicKeyOrFail(UUID repositoryId);
102
103        void addLocalRepoManagerCloseListener(LocalRepoManagerCloseListener listener);
104
105        void removeLocalRepoManagerCloseListener(LocalRepoManagerCloseListener listener);
106
107        /**
108         * Gets the <i>open</i> state.
109         * <p>
110         * If this is <code>false</code>, the {@link LocalRepoManager} instance cannot be used anymore.
111         * Due to the proxy-mechanism, this does, however, not mean that the backend is really shut down.
112         * @return the <i>open</i> state.
113         */
114        boolean isOpen();
115
116        /**
117         * Closes this {@link LocalRepoManager}.
118         * <p>
119         * <b>Important:</b> The {@link LocalRepoManagerFactory} always returns a proxy. It never returns
120         * the real backend-instance. Calling {@code close()} closes the proxy and thus renders it unusable.
121         * It decrements the real backend-instance's reference counter. As soon as this reaches 0, the backend
122         * is really closed - which may happen delayed (for performance reasons).
123         */
124        @Override
125        void close();
126
127        /**
128         * Begin a JDO transaction for read operations only in the underlying database.
129         * @return the transaction handle. Never <code>null</code>.
130         */
131        LocalRepoTransaction beginReadTransaction();
132
133        /**
134         * Begin a JDO transaction for read and write operations in the underlying database.
135         * @return the transaction handle. Never <code>null</code>.
136         */
137        LocalRepoTransaction beginWriteTransaction();
138
139        /**
140         * Synchronises the local file system with the local database.
141         * <p>
142         * Registers every directory and file in the repository's {@link #getLocalRoot() local root} and its
143         * sub-directories.
144         */
145        void localSync(ProgressMonitor monitor);
146
147        /**
148         * Adds or relocates a remote repository.
149         * @param repositoryId the remote repository's unique ID. Must not be <code>null</code>. This is
150         * {@link LocalRepository#getEntityID() LocalRepository.entityID} in the remote database and will become
151         * {@link RemoteRepository#getEntityID() RemoteRepository.entityID} in the local database.
152         * @param remoteRoot the URL of the remote repository. May be <code>null</code> (in the server, a
153         * {@code RemoteRepository} never has a {@code remoteRoot}).
154         * @param localPathPrefix TODO
155         */
156        void putRemoteRepository(UUID repositoryId, URL remoteRoot, byte[] publicKey, String localPathPrefix);
157
158        /**
159         * Deletes a remote repository from the local database.
160         * <p>
161         * Does nothing, if the specified repository does not exist.
162         * @param repositoryId the remote repository's unique ID. Must not be <code>null</code>.
163         */
164        void deleteRemoteRepository(UUID repositoryId);
165
166        Map<UUID, URL> getRemoteRepositoryId2RemoteRootMap();
167
168        /**
169         * Gets the local path-prefix (of the local repository managed by this {@code LocalRepoManager}) when syncing with
170         * the remote repository identified by the given {@code remoteRoot}.
171         * @param remoteRoot the remote repository's root-URL (not necessarily its real root, but the root URL connected
172         * to the local repository). Must not be <code>null</code>.
173         * @return the local path-prefix. Never <code>null</code>, but maybe empty.
174         * @throws IllegalArgumentException if there is no remote-repository with the given {@code remoteRoot}.
175         */
176        String getLocalPathPrefixOrFail(URL remoteRoot);
177
178        /**
179         * Gets the local path-prefix (of the local repository managed by this {@code LocalRepoManager}) when syncing with
180         * the remote repository identified by the given {@code remoteRoot}.
181         * @param repositoryId the remote repository's unique ID. Must not be <code>null</code>.
182         * @return the local path-prefix. Never <code>null</code>, but maybe empty.
183         * @throws IllegalArgumentException if there is no remote-repository with the given {@code remoteRoot}.
184         */
185        String getLocalPathPrefixOrFail(UUID repositoryId);
186
187        /**
188         * Gets the unique ID of the remote repository identified by the given {@code remoteRoot}.
189         * @param remoteRoot the remote repository's root-URL (not necessarily its real root, but the root URL connected
190         * to the local repository). Must not be <code>null</code>.
191         * @return the remote repository's unique ID. Never <code>null</code>.
192         * @throws IllegalArgumentException if there is no remote-repository with the given {@code remoteRoot}.
193         */
194        UUID getRemoteRepositoryIdOrFail(URL remoteRoot);
195
196        Lock getLock();
197
198        /**
199         * @deprecated <b>Do not invoke this method directly!</b> It is declared in this interface to make sure the
200         * proxy's {@link InvocationHandler} is invoked when the garbage-collector collects the proxy.
201         */
202        @Deprecated
203        void finalize() throws Throwable;
204
205        void putRepositoryAlias(String repositoryAlias);
206
207        void removeRepositoryAlias(String repositoryAlias);
208
209        LocalRepoMetaData getLocalRepoMetaData();
210}