From da5846209f76100df8497efca3515b9aedf50cdb Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Fri, 14 Jun 2024 12:42:59 +0200 Subject: [PATCH] Merge pull request #18402 from overleaf/msm-git-bridge-pom * [git-bridge] Extracted pom versions to properties Managing versions as properties is a best practice with pom files. An advantage in our case is ensuring dependency groups (as simpleclient) are upgraded together. * [git-bridge] Remove '_' as keyword * [git-bridge] Added formatting with 'fmt-maven-plugin' * Remove java from .editorconfig No longer needed as a formatter is now available * Replace javadoc comments with multiline comments Replaces "/**" with "/*", which then prevents the formatter from adding HTML elements to comments * [git-bridge] Formatted .java files GitOrigin-RevId: 0997b838cee88c290d824a74444295e26392f26b --- services/git-bridge/Makefile | 9 + services/git-bridge/pom.xml | 97 +- .../main/java/uk/ac/ic/wlgitbridge/Main.java | 34 +- .../wlgitbridge/application/GitBridgeApp.java | 143 +- .../application/config/Config.java | 364 ++- .../application/config/Oauth2.java | 51 +- .../application/exception/ArgsException.java | 2 +- .../exception/ConfigFileException.java | 17 +- .../application/jetty/NullLogger.java | 99 +- .../uk/ac/ic/wlgitbridge/bridge/Bridge.java | 1248 ++++---- .../bridge/db/DBInitException.java | 21 +- .../ac/ic/wlgitbridge/bridge/db/DBStore.java | 48 +- .../wlgitbridge/bridge/db/ProjectState.java | 10 +- .../bridge/db/noop/NoopDbStore.java | 104 +- .../bridge/db/sqlite/SQLQuery.java | 5 +- .../bridge/db/sqlite/SQLUpdate.java | 10 +- .../bridge/db/sqlite/SqliteDBStore.java | 407 ++- .../GetLatestVersionForProjectSQLQuery.java | 51 +- .../db/sqlite/query/GetNumProjects.java | 32 +- .../sqlite/query/GetNumUnswappedProjects.java | 34 +- .../db/sqlite/query/GetOldestProjectName.java | 36 +- .../query/GetPathForURLInProjectSQLQuery.java | 61 +- .../sqlite/query/GetProjectNamesSQLQuery.java | 33 +- .../db/sqlite/query/GetProjectState.java | 64 +- .../db/sqlite/query/GetSwapCompression.java | 9 +- .../query/LastAccessedColumnExists.java | 35 +- .../sqlite/query/RestoreTimeColumnExists.java | 30 +- .../query/SwapCompressionColumnExists.java | 33 +- .../db/sqlite/query/SwapTimeColumnExists.java | 33 +- .../update/alter/ProjectsAddLastAccessed.java | 16 +- .../update/alter/ProjectsAddRestoreTime.java | 3 +- .../alter/ProjectsAddSwapCompression.java | 3 +- .../update/alter/ProjectsAddSwapTime.java | 4 +- .../update/alter/SetSoftHeapLimitPragma.java | 17 +- .../create/CreateIndexURLIndexStore.java | 17 +- .../CreateProjectsIndexLastAccessed.java | 17 +- .../create/CreateProjectsTableSQLUpdate.java | 31 +- .../create/CreateURLIndexStoreSQLUpdate.java | 35 +- .../DeleteAllFilesInProjectSQLUpdate.java | 1 - .../DeleteFilesForProjectSQLUpdate.java | 66 +- .../update/delete/DeleteProjectSQLUpdate.java | 1 - .../update/insert/AddURLIndexSQLUpdate.java | 58 +- .../insert/SetProjectLastAccessedTime.java | 47 +- .../update/insert/SetProjectSQLUpdate.java | 46 +- .../sqlite/update/insert/UpdateRestore.java | 19 +- .../db/sqlite/update/insert/UpdateSwap.java | 19 +- .../uk/ac/ic/wlgitbridge/bridge/gc/GcJob.java | 19 +- .../ic/wlgitbridge/bridge/gc/GcJobImpl.java | 218 +- .../ic/wlgitbridge/bridge/lock/LockGuard.java | 5 +- .../wlgitbridge/bridge/lock/ProjectLock.java | 19 +- .../bridge/repo/FSGitRepoStore.java | 301 +- .../bridge/repo/GitProjectRepo.java | 415 ++- .../bridge/repo/NoGitignoreIterator.java | 110 +- .../wlgitbridge/bridge/repo/ProjectRepo.java | 35 +- .../ic/wlgitbridge/bridge/repo/RepoStore.java | 132 +- .../bridge/repo/RepoStoreConfig.java | 30 +- .../bridge/repo/WalkOverrideGitRepo.java | 128 +- .../bridge/resource/ResourceCache.java | 24 +- .../bridge/resource/UrlResourceCache.java | 207 +- .../bridge/snapshot/NetSnapshotApi.java | 58 +- .../bridge/snapshot/SnapshotApi.java | 61 +- .../bridge/snapshot/SnapshotApiFacade.java | 223 +- .../bridge/swap/job/NoopSwapJob.java | 19 +- .../wlgitbridge/bridge/swap/job/SwapJob.java | 207 +- .../bridge/swap/job/SwapJobConfig.java | 91 +- .../bridge/swap/job/SwapJobImpl.java | 443 ++- .../bridge/swap/store/InMemorySwapStore.java | 67 +- .../bridge/swap/store/NoopSwapStore.java | 32 +- .../bridge/swap/store/S3SwapStore.java | 112 +- .../bridge/swap/store/SwapStore.java | 54 +- .../bridge/swap/store/SwapStoreConfig.java | 113 +- .../ic/wlgitbridge/bridge/util/CastUtil.java | 16 +- .../wlgitbridge/data/CandidateSnapshot.java | 214 +- .../data/CannotAcquireLockException.java | 8 +- .../ac/ic/wlgitbridge/data/LockAllWaiter.java | 5 +- .../ic/wlgitbridge/data/ProjectLockImpl.java | 146 +- .../ac/ic/wlgitbridge/data/ServletFile.java | 62 +- .../data/filestore/GitDirectoryContents.java | 122 +- .../data/filestore/RawDirectory.java | 20 +- .../wlgitbridge/data/filestore/RawFile.java | 55 +- .../data/filestore/RepositoryFile.java | 39 +- .../ic/wlgitbridge/data/model/Snapshot.java | 100 +- .../exception/FileLimitExceededException.java | 41 +- .../git/exception/GitUserException.java | 7 +- .../git/exception/InvalidGitRepository.java | 24 +- .../exception/SizeLimitExceededException.java | 46 +- .../git/exception/SnapshotAPIException.java | 6 +- .../git/handler/WLReceivePackFactory.java | 97 +- .../git/handler/WLRepositoryResolver.java | 175 +- .../git/handler/WLUploadPackFactory.java | 44 +- .../git/handler/hook/WriteLatexPutHook.java | 251 +- .../hook/exception/ForcedPushException.java | 42 +- .../hook/exception/WrongBranchException.java | 35 +- .../wlgitbridge/git/servlet/WLGitServlet.java | 46 +- .../git/servlet/WLGitServletConfig.java | 50 +- .../git/util/RepositoryObjectTreeWalker.java | 135 +- .../io/http/ning/NingHttpClient.java | 96 +- .../io/http/ning/NingHttpClientFacade.java | 24 +- .../server/BasicAuthCredentials.java | 1 - .../server/DiagnosticsHandler.java | 46 +- .../ac/ic/wlgitbridge/server/FileHandler.java | 80 +- .../wlgitbridge/server/GitBridgeServer.java | 292 +- .../ic/wlgitbridge/server/GitLfsHandler.java | 34 +- .../server/HealthCheckHandler.java | 29 +- .../ic/wlgitbridge/server/Oauth2Filter.java | 613 ++-- .../wlgitbridge/server/PostbackContents.java | 111 +- .../wlgitbridge/server/PostbackHandler.java | 129 +- .../server/ProductionErrorHandler.java | 27 +- .../server/ProjectDeletionHandler.java | 12 +- .../wlgitbridge/server/PrometheusHandler.java | 43 +- .../ic/wlgitbridge/server/StatusHandler.java | 30 +- .../snapshot/base/ForbiddenException.java | 26 +- .../wlgitbridge/snapshot/base/HTTPMethod.java | 8 +- .../wlgitbridge/snapshot/base/JSONSource.java | 5 +- .../base/MissingRepositoryException.java | 154 +- .../ic/wlgitbridge/snapshot/base/Request.java | 340 ++- .../ic/wlgitbridge/snapshot/base/Result.java | 48 +- .../snapshot/base/SnapshotAPIRequest.java | 49 +- .../exception/FailedConnectionException.java | 16 +- .../snapshot/getdoc/GetDocRequest.java | 44 +- .../snapshot/getdoc/GetDocResult.java | 199 +- .../exception/InvalidProjectException.java | 46 +- .../getforversion/GetForVersionRequest.java | 57 +- .../getforversion/GetForVersionResult.java | 41 +- .../getforversion/SnapshotAttachment.java | 67 +- .../snapshot/getforversion/SnapshotData.java | 117 +- .../snapshot/getforversion/SnapshotFile.java | 103 +- .../getsavedvers/GetSavedVersRequest.java | 40 +- .../getsavedvers/GetSavedVersResult.java | 86 +- .../snapshot/getsavedvers/SnapshotInfo.java | 104 +- .../snapshot/getsavedvers/WLUser.java | 50 +- .../snapshot/push/PostbackManager.java | 136 +- .../snapshot/push/PostbackPromise.java | 131 +- .../snapshot/push/PushRequest.java | 60 +- .../wlgitbridge/snapshot/push/PushResult.java | 61 +- .../exception/InternalErrorException.java | 31 +- .../push/exception/InvalidFilesException.java | 105 +- .../InvalidPostbackKeyException.java | 2 +- .../exception/InvalidProjectException.java | 49 +- .../push/exception/OutOfDateException.java | 32 +- .../exception/PostbackTimeoutException.java | 41 +- .../SevereSnapshotPostException.java | 15 +- .../push/exception/SnapshotPostException.java | 11 +- .../SnapshotPostExceptionBuilder.java | 39 +- .../exception/UnexpectedErrorException.java | 41 +- .../UnexpectedPostbackException.java | 2 +- .../wlgitbridge/snapshot/servermock/Main.java | 30 +- .../exception/InvalidAPICallException.java | 9 +- .../servermock/response/SnapshotResponse.java | 11 +- .../response/SnapshotResponseBuilder.java | 110 +- .../getdoc/SnapshotGetDocResponse.java | 21 +- .../getforver/SnapshotGetForVerResponse.java | 21 +- .../SnapshotGetSavedVersResponse.java | 21 +- .../response/push/SnapshotPushResponse.java | 47 +- .../push/data/SnapshotPushResult.java | 35 +- .../data/SnapshotPushResultOutOfDate.java | 17 +- .../push/data/SnapshotPushResultSuccess.java | 17 +- .../postback/SnapshotPostbackRequest.java | 21 +- .../SnapshotPostbackRequestError.java | 21 +- .../SnapshotPostbackRequestInvalidFiles.java | 48 +- ...SnapshotPostbackRequestInvalidProject.java | 49 +- .../SnapshotPostbackRequestOutOfDate.java | 21 +- .../SnapshotPostbackRequestSuccess.java | 25 +- .../invalidfile/InvalidFileError.java | 59 +- .../invalidfile/InvalidFileErrorDefault.java | 17 +- .../InvalidFileErrorDisallowed.java | 17 +- .../invalidfile/InvalidFileErrorUnclean.java | 33 +- .../server/MockOAuthRequestHandler.java | 29 +- .../server/MockSnapshotRequestHandler.java | 76 +- .../servermock/server/MockSnapshotServer.java | 88 +- .../servermock/server/PostbackThread.java | 67 +- .../servermock/state/SnapshotAPIState.java | 414 +-- .../state/SnapshotAPIStateBuilder.java | 304 +- .../snapshot/servermock/util/FileUtil.java | 279 +- .../ac/ic/wlgitbridge/util/BiConsumerT.java | 5 +- .../util/DeletingFileInputStream.java | 86 +- .../java/uk/ac/ic/wlgitbridge/util/Files.java | 172 +- .../uk/ac/ic/wlgitbridge/util/FunctionT.java | 5 +- .../uk/ac/ic/wlgitbridge/util/Instance.java | 20 +- .../java/uk/ac/ic/wlgitbridge/util/Log.java | 113 +- .../uk/ac/ic/wlgitbridge/util/Project.java | 18 +- .../ac/ic/wlgitbridge/util/ResourceUtil.java | 43 +- .../java/uk/ac/ic/wlgitbridge/util/Tar.java | 306 +- .../uk/ac/ic/wlgitbridge/util/TimerUtils.java | 27 +- .../java/uk/ac/ic/wlgitbridge/util/Util.java | 310 +- .../WLGitBridgeIntegrationTest.java | 2550 ++++++++++------- .../application/config/ConfigTest.java | 196 +- .../ac/ic/wlgitbridge/bridge/BridgeTest.java | 146 +- .../bridge/db/sqlite/SqliteDBStoreTest.java | 256 +- .../DeleteFilesForProjectSQLUpdateTest.java | 29 +- .../wlgitbridge/bridge/gc/GcJobImplTest.java | 188 +- .../bridge/repo/FSGitRepoStoreTest.java | 143 +- .../bridge/repo/GitProjectRepoTest.java | 316 +- .../bridge/resource/UrlResourceCacheTest.java | 177 +- .../bridge/swap/job/SwapJobImplTest.java | 272 +- .../swap/store/InMemorySwapStoreTest.java | 120 +- .../bridge/swap/store/S3SwapStoreTest.java | 56 +- .../data/model/ResourceFetcherTest.java | 89 +- .../snapshot/push/PostbackManagerTest.java | 81 +- .../servermock/util/FileUtilTest.java | 163 +- .../ac/ic/wlgitbridge/util/ProjectTest.java | 15 +- .../uk/ac/ic/wlgitbridge/util/TarTest.java | 110 +- .../ic/wlgitbridge/util/TimerUtilsTest.java | 19 +- 203 files changed, 9548 insertions(+), 10742 deletions(-) diff --git a/services/git-bridge/Makefile b/services/git-bridge/Makefile index 3a24af8ad8..2fd30f0fa5 100644 --- a/services/git-bridge/Makefile +++ b/services/git-bridge/Makefile @@ -16,6 +16,15 @@ $(MVN_TARGET): $(shell find src -type f) pom.xml build: $(MVN_TARGET) + +format: + mvn $(MVN_OPTS) com.spotify.fmt:fmt-maven-plugin:check + + +format_fix: + mvn $(MVN_OPTS) com.spotify.fmt:fmt-maven-plugin:format + + test: mvn $(MVN_OPTS) test diff --git a/services/git-bridge/pom.xml b/services/git-bridge/pom.xml index 6f4b3f2f72..16fba4a94e 100644 --- a/services/git-bridge/pom.xml +++ b/services/git-bridge/pom.xml @@ -8,19 +8,43 @@ writelatex-git-bridge 1.0-SNAPSHOT + 1.8 UTF-8 - + 3.7.0 + 2.12.4 + 3.1.0 + 2.23 + 4.13.2 + 2.8.4 + 9.4.51.v20230217 + 2.9.0 + 2.12.3 + 6.6.1.202309021850-r + 3.41.2.2 + 2.9.9 + 1.34.1 + 1.23.0 + 3.12.0 + 1.2.3 + 5.12.0 + 3.11.1 + 1.11.274 + ${jaxb.runtime.version} + 2.3.2 + 4.5.14 + 2.10.0 + 1.24.0 + 0.10.0 maven-compiler-plugin - 3.7.0 + ${maven.compiler.plugin.version} - 1.8 - 1.8 - + ${java.version} + ${java.version} @@ -28,7 +52,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.12.4 + ${maven.surefire.plugin.version} -Djdk.net.URLClassPath.disableClassPathURLCheck=true @@ -36,7 +60,7 @@ maven-assembly-plugin - 3.1.0 + ${maven.assembly.plugin.version} package @@ -56,6 +80,11 @@ + + com.spotify.fmt + fmt-maven-plugin + ${fmt.plugin.version} + @@ -63,169 +92,169 @@ junit junit - 4.13.2 + ${junit.version} test org.jmock jmock-junit4 - 2.8.4 + ${jmock.junit4.version} test org.eclipse.jetty jetty-servlet - 9.4.51.v20230217 + ${jetty.servlet.version} org.eclipse.jetty jetty-server - 9.4.51.v20230217 + ${jetty.servlet.version} com.google.code.gson gson - 2.9.0 + ${gson.version} org.asynchttpclient async-http-client - 2.12.3 + ${async.http.client.version} org.eclipse.jgit org.eclipse.jgit - 6.6.1.202309021850-r + ${jgit.version} org.eclipse.jgit org.eclipse.jgit.http.server - 6.6.1.202309021850-r + ${jgit.version} org.xerial sqlite-jdbc - 3.41.2.2 + ${sqlite.jdbc.version} joda-time joda-time - 2.9.9 + ${joda.time.version} com.google.oauth-client google-oauth-client - 1.34.1 + ${google.oauth.client.version} com.google.http-client google-http-client - 1.23.0 + ${google.http.client.version} com.google.http-client google-http-client-gson - 1.23.0 + ${google.http.client.version} org.apache.commons commons-lang3 - 3.12.0 + ${commons.lang3.version} ch.qos.logback logback-classic - 1.2.3 + ${logback.classic.version} org.mock-server mockserver-netty - 5.12.0 + ${mockserver.version} test org.mock-server mockserver-junit-rule - 5.12.0 + ${mockserver.version} test org.mockito mockito-core - 3.11.1 + ${mockito.version} test com.amazonaws aws-java-sdk - 1.11.274 + ${aws.java.sdk.version} jakarta.xml.bind jakarta.xml.bind-api - 2.3.2 + ${jakarta.xml.bind.api.version} org.glassfish.jaxb jaxb-runtime - 2.3.2 + ${jaxb.runtime.version} org.apache.httpcomponents httpclient - 4.5.14 + ${httpclient.version} commons-io commons-io - 2.10.0 + ${commons.io.version} org.apache.commons commons-compress - 1.24.0 + ${commons.compress.version} io.prometheus simpleclient - 0.10.0 + ${simpleclient.version} io.prometheus simpleclient_hotspot - 0.10.0 + ${simpleclient.version} io.prometheus simpleclient_servlet - 0.10.0 + ${simpleclient.version} diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/Main.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/Main.java index f56a3a80e3..5e365b893b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/Main.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/Main.java @@ -1,17 +1,14 @@ package uk.ac.ic.wlgitbridge; +import java.util.Arrays; import uk.ac.ic.wlgitbridge.application.GitBridgeApp; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.server.GitBridgeServer; import uk.ac.ic.wlgitbridge.util.Log; -import java.util.Arrays; - -/** +/* * Created by Winston on 01/11/14. */ -/** +/* * This is the entry point into the Git Bridge. * * It is responsible for creating the {@link GitBridgeApp} and then running it. @@ -29,21 +26,14 @@ import java.util.Arrays; */ public class Main { - public static void main(String[] args) { - Log.info( - "Git Bridge started with args: " - + Arrays.toString(args) - ); - try { - new GitBridgeApp(args).run(); - } catch (Throwable t) { - /* So that we get a timestamp */ - Log.error( - "Fatal exception thrown to top level, exiting: ", - t - ); - System.exit(1); - } + public static void main(String[] args) { + Log.info("Git Bridge started with args: " + Arrays.toString(args)); + try { + new GitBridgeApp(args).run(); + } catch (Throwable t) { + /* So that we get a timestamp */ + Log.error("Fatal exception thrown to top level, exiting: ", t); + System.exit(1); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java index a184effde4..e3cb95226d 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java @@ -1,102 +1,93 @@ package uk.ac.ic.wlgitbridge.application; +import java.io.IOException; +import javax.servlet.ServletException; import uk.ac.ic.wlgitbridge.application.config.Config; import uk.ac.ic.wlgitbridge.application.exception.ArgsException; import uk.ac.ic.wlgitbridge.application.exception.ConfigFileException; import uk.ac.ic.wlgitbridge.server.GitBridgeServer; import uk.ac.ic.wlgitbridge.util.Log; -import javax.servlet.ServletException; -import java.io.IOException; - -/** +/* * Created by Winston on 02/11/14. */ -/** +/* * Class that represents the application. Parses arguments and gives them to the * server, or dies with a usage message. */ public class GitBridgeApp implements Runnable { - public static final int EXIT_CODE_FAILED = 1; - private static final String USAGE_MESSAGE = - "usage: writelatex-git-bridge [config_file]"; + public static final int EXIT_CODE_FAILED = 1; + private static final String USAGE_MESSAGE = "usage: writelatex-git-bridge [config_file]"; - private String configFilePath; - Config config; - private GitBridgeServer server; + private String configFilePath; + Config config; + private GitBridgeServer server; - /** - * Constructs an instance of the WriteLatex-Git Bridge application. - * @param args args from main, which should be in the format [config_file] - */ - public GitBridgeApp(String[] args) { - try { - parseArguments(args); - loadConfigFile(); - Log.info("Config loaded: {}", config.getSanitisedString()); - } catch (ArgsException e) { - printUsage(); - System.exit(EXIT_CODE_FAILED); - } catch (ConfigFileException e) { - Log.error( - "The property for " + - e.getMissingMember() + - " is invalid. Check your config file." - ); - System.exit(EXIT_CODE_FAILED); - } catch (IOException e) { - Log.error("Invalid config file. Check the file path."); - System.exit(EXIT_CODE_FAILED); - } - try { - server = new GitBridgeServer(config); - } catch (ServletException e) { - Log.error( - "Servlet exception when instantiating GitBridgeServer", - e - ); - } + /* + * Constructs an instance of the WriteLatex-Git Bridge application. + * @param args args from main, which should be in the format [config_file] + */ + public GitBridgeApp(String[] args) { + try { + parseArguments(args); + loadConfigFile(); + Log.info("Config loaded: {}", config.getSanitisedString()); + } catch (ArgsException e) { + printUsage(); + System.exit(EXIT_CODE_FAILED); + } catch (ConfigFileException e) { + Log.error( + "The property for " + e.getMissingMember() + " is invalid. Check your config file."); + System.exit(EXIT_CODE_FAILED); + } catch (IOException e) { + Log.error("Invalid config file. Check the file path."); + System.exit(EXIT_CODE_FAILED); } - - /** - * Starts the server with the port number and root directory path given in - * the command-line arguments. - */ - @Override - public void run() { - server.start(); + try { + server = new GitBridgeServer(config); + } catch (ServletException e) { + Log.error("Servlet exception when instantiating GitBridgeServer", e); } + } - public void stop() { - server.stop(); + /* + * Starts the server with the port number and root directory path given in + * the command-line arguments. + */ + @Override + public void run() { + server.start(); + } + + public void stop() { + server.stop(); + } + + /* Helper methods */ + + private void parseArguments(String[] args) throws ArgsException { + checkArgumentsLength(args); + parseConfigFilePath(args); + } + + private void checkArgumentsLength(String[] args) throws ArgsException { + if (args.length < 1) { + throw new ArgsException(); } + } - /* Helper methods */ + private void parseConfigFilePath(String[] args) throws ArgsException { + configFilePath = args[0]; + } - private void parseArguments(String[] args) throws ArgsException { - checkArgumentsLength(args); - parseConfigFilePath(args); - } - - private void checkArgumentsLength(String[] args) throws ArgsException { - if (args.length < 1) { - throw new ArgsException(); - } - } - - private void parseConfigFilePath(String[] args) throws ArgsException { - configFilePath = args[0]; - } - - private void loadConfigFile() throws ConfigFileException, IOException { - Log.info("Loading config file at path: " + configFilePath); - config = new Config(configFilePath); - } - - private void printUsage() { - System.err.println(USAGE_MESSAGE); - } + private void loadConfigFile() throws ConfigFileException, IOException { + Log.info("Loading config file at path: " + configFilePath); + config = new Config(configFilePath); + } + private void printUsage() { + System.err.println(USAGE_MESSAGE); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Config.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Config.java index af1850188d..cf36916600 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Config.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Config.java @@ -3,6 +3,11 @@ package uk.ac.ic.wlgitbridge.application.config; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Optional; +import javax.annotation.Nullable; import uk.ac.ic.wlgitbridge.application.exception.ConfigFileException; import uk.ac.ic.wlgitbridge.bridge.repo.RepoStoreConfig; import uk.ac.ic.wlgitbridge.bridge.swap.job.SwapJobConfig; @@ -10,211 +15,182 @@ import uk.ac.ic.wlgitbridge.bridge.swap.store.SwapStoreConfig; import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; import uk.ac.ic.wlgitbridge.util.Instance; -import javax.annotation.Nullable; -import java.io.FileReader; -import java.io.IOException; -import java.io.Reader; -import java.util.Optional; - -/** +/* * Created by Winston on 05/12/14. */ public class Config implements JSONSource { - static Config asSanitised(Config config) { - return new Config( - config.port, - config.bindIp, - config.idleTimeout, - config.rootGitDirectory, - config.apiBaseURL, - config.postbackURL, - config.serviceName, - Oauth2.asSanitised(config.oauth2), - config.userPasswordEnabled, - config.repoStore, - SwapStoreConfig.sanitisedCopy(config.swapStore), - config.swapJob, - config.sqliteHeapLimitBytes - ); + static Config asSanitised(Config config) { + return new Config( + config.port, + config.bindIp, + config.idleTimeout, + config.rootGitDirectory, + config.apiBaseURL, + config.postbackURL, + config.serviceName, + Oauth2.asSanitised(config.oauth2), + config.userPasswordEnabled, + config.repoStore, + SwapStoreConfig.sanitisedCopy(config.swapStore), + config.swapJob, + config.sqliteHeapLimitBytes); + } + + private int port; + private String bindIp; + private int idleTimeout; + private String rootGitDirectory; + private String apiBaseURL; + private String postbackURL; + private String serviceName; + @Nullable private Oauth2 oauth2; + private boolean userPasswordEnabled; + @Nullable private RepoStoreConfig repoStore; + @Nullable private SwapStoreConfig swapStore; + @Nullable private SwapJobConfig swapJob; + private int sqliteHeapLimitBytes = 0; + + public Config(String configFilePath) throws ConfigFileException, IOException { + this(new FileReader(configFilePath)); + } + + Config(Reader reader) { + fromJSON(new Gson().fromJson(reader, JsonElement.class)); + } + + public Config( + int port, + String bindIp, + int idleTimeout, + String rootGitDirectory, + String apiBaseURL, + String postbackURL, + String serviceName, + Oauth2 oauth2, + boolean userPasswordEnabled, + RepoStoreConfig repoStore, + SwapStoreConfig swapStore, + SwapJobConfig swapJob, + int sqliteHeapLimitBytes) { + this.port = port; + this.bindIp = bindIp; + this.idleTimeout = idleTimeout; + this.rootGitDirectory = rootGitDirectory; + this.apiBaseURL = apiBaseURL; + this.postbackURL = postbackURL; + this.serviceName = serviceName; + this.oauth2 = oauth2; + this.userPasswordEnabled = userPasswordEnabled; + this.repoStore = repoStore; + this.swapStore = swapStore; + this.swapJob = swapJob; + this.sqliteHeapLimitBytes = sqliteHeapLimitBytes; + } + + @Override + public void fromJSON(JsonElement json) { + JsonObject configObject = json.getAsJsonObject(); + port = getElement(configObject, "port").getAsInt(); + bindIp = getElement(configObject, "bindIp").getAsString(); + idleTimeout = getElement(configObject, "idleTimeout").getAsInt(); + rootGitDirectory = getElement(configObject, "rootGitDirectory").getAsString(); + String apiBaseURL = getElement(configObject, "apiBaseUrl").getAsString(); + if (!apiBaseURL.endsWith("/")) { + apiBaseURL += "/"; } - - private int port; - private String bindIp; - private int idleTimeout; - private String rootGitDirectory; - private String apiBaseURL; - private String postbackURL; - private String serviceName; - @Nullable - private Oauth2 oauth2; - private boolean userPasswordEnabled; - @Nullable - private RepoStoreConfig repoStore; - @Nullable - private SwapStoreConfig swapStore; - @Nullable - private SwapJobConfig swapJob; - private int sqliteHeapLimitBytes = 0; - - public Config( - String configFilePath - ) throws ConfigFileException, - IOException { - this(new FileReader(configFilePath)); + this.apiBaseURL = apiBaseURL; + serviceName = getElement(configObject, "serviceName").getAsString(); + postbackURL = getElement(configObject, "postbackBaseUrl").getAsString(); + if (!postbackURL.endsWith("/")) { + postbackURL += "/"; } - - Config(Reader reader) { - fromJSON(new Gson().fromJson(reader, JsonElement.class)); + oauth2 = new Gson().fromJson(configObject.get("oauth2"), Oauth2.class); + userPasswordEnabled = getOptionalString(configObject, "userPasswordEnabled").equals("true"); + repoStore = new Gson().fromJson(configObject.get("repoStore"), RepoStoreConfig.class); + swapStore = new Gson().fromJson(configObject.get("swapStore"), SwapStoreConfig.class); + swapJob = new Gson().fromJson(configObject.get("swapJob"), SwapJobConfig.class); + if (configObject.has("sqliteHeapLimitBytes")) { + sqliteHeapLimitBytes = getElement(configObject, "sqliteHeapLimitBytes").getAsInt(); } + } - public Config( - int port, - String bindIp, - int idleTimeout, - String rootGitDirectory, - String apiBaseURL, - String postbackURL, - String serviceName, - Oauth2 oauth2, - boolean userPasswordEnabled, - RepoStoreConfig repoStore, - SwapStoreConfig swapStore, - SwapJobConfig swapJob, - int sqliteHeapLimitBytes - ) { - this.port = port; - this.bindIp = bindIp; - this.idleTimeout = idleTimeout; - this.rootGitDirectory = rootGitDirectory; - this.apiBaseURL = apiBaseURL; - this.postbackURL = postbackURL; - this.serviceName = serviceName; - this.oauth2 = oauth2; - this.userPasswordEnabled = userPasswordEnabled; - this.repoStore = repoStore; - this.swapStore = swapStore; - this.swapJob = swapJob; - this.sqliteHeapLimitBytes = sqliteHeapLimitBytes; + public String getSanitisedString() { + return Instance.prettyGson.toJson(Config.asSanitised(this)); + } + + public int getPort() { + return port; + } + + public String getBindIp() { + return bindIp; + } + + public int getIdleTimeout() { + return idleTimeout; + } + + public String getRootGitDirectory() { + return rootGitDirectory; + } + + public int getSqliteHeapLimitBytes() { + return this.sqliteHeapLimitBytes; + } + + public String getAPIBaseURL() { + return apiBaseURL; + } + + public String getServiceName() { + return serviceName; + } + + public String getPostbackURL() { + return postbackURL; + } + + public boolean isUsingOauth2() { + return oauth2 != null; + } + + public boolean isUserPasswordEnabled() { + return userPasswordEnabled; + } + + public Oauth2 getOauth2() { + if (!isUsingOauth2()) { + throw new AssertionError("Getting oauth2 when not using it"); } + return oauth2; + } - @Override - public void fromJSON(JsonElement json) { - JsonObject configObject = json.getAsJsonObject(); - port = getElement(configObject, "port").getAsInt(); - bindIp = getElement(configObject, "bindIp").getAsString(); - idleTimeout = getElement(configObject, "idleTimeout").getAsInt(); - rootGitDirectory = getElement( - configObject, - "rootGitDirectory" - ).getAsString(); - String apiBaseURL = getElement( - configObject, - "apiBaseUrl" - ).getAsString(); - if (!apiBaseURL.endsWith("/")) { - apiBaseURL += "/"; - } - this.apiBaseURL = apiBaseURL; - serviceName = getElement(configObject, "serviceName").getAsString(); - postbackURL = getElement(configObject, "postbackBaseUrl").getAsString(); - if (!postbackURL.endsWith("/")) { - postbackURL += "/"; - } - oauth2 = new Gson().fromJson(configObject.get("oauth2"), Oauth2.class); - userPasswordEnabled = getOptionalString(configObject, "userPasswordEnabled").equals("true"); - repoStore = new Gson().fromJson( - configObject.get("repoStore"), RepoStoreConfig.class); - swapStore = new Gson().fromJson( - configObject.get("swapStore"), - SwapStoreConfig.class - ); - swapJob = new Gson().fromJson( - configObject.get("swapJob"), - SwapJobConfig.class - ); - if (configObject.has("sqliteHeapLimitBytes")) { - sqliteHeapLimitBytes = getElement(configObject, "sqliteHeapLimitBytes").getAsInt(); - } + public Optional getRepoStore() { + return Optional.ofNullable(repoStore); + } + + public Optional getSwapStore() { + return Optional.ofNullable(swapStore); + } + + public Optional getSwapJob() { + return Optional.ofNullable(swapJob); + } + + private JsonElement getElement(JsonObject configObject, String name) { + JsonElement element = configObject.get(name); + if (element == null) { + throw new RuntimeException(new ConfigFileException(name)); } + return element; + } - public String getSanitisedString() { - return Instance.prettyGson.toJson(Config.asSanitised(this)); + private String getOptionalString(JsonObject configObject, String name) { + JsonElement element = configObject.get(name); + if (element == null || !element.isJsonPrimitive()) { + return ""; } - - public int getPort() { - return port; - } - - public String getBindIp() { - return bindIp; - } - - public int getIdleTimeout() { - return idleTimeout; - } - - public String getRootGitDirectory() { - return rootGitDirectory; - } - - public int getSqliteHeapLimitBytes() { - return this.sqliteHeapLimitBytes; - } - - public String getAPIBaseURL() { - return apiBaseURL; - } - - public String getServiceName() { - return serviceName; - } - - public String getPostbackURL() { - return postbackURL; - } - - public boolean isUsingOauth2() { - return oauth2 != null; - } - - public boolean isUserPasswordEnabled() { - return userPasswordEnabled; - } - - public Oauth2 getOauth2() { - if (!isUsingOauth2()) { - throw new AssertionError("Getting oauth2 when not using it"); - } - return oauth2; - } - - public Optional getRepoStore() { - return Optional.ofNullable(repoStore); - } - - public Optional getSwapStore() { - return Optional.ofNullable(swapStore); - } - - public Optional getSwapJob() { - return Optional.ofNullable(swapJob); - } - - private JsonElement getElement(JsonObject configObject, String name) { - JsonElement element = configObject.get(name); - if (element == null) { - throw new RuntimeException(new ConfigFileException(name)); - } - return element; - } - - private String getOptionalString(JsonObject configObject, String name) { - JsonElement element = configObject.get(name); - if (element == null || !element.isJsonPrimitive()) { - return ""; - } - return element.getAsString(); - } - + return element.getAsString(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Oauth2.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Oauth2.java index daf1170c82..1db7d3b4d2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Oauth2.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Oauth2.java @@ -1,42 +1,33 @@ package uk.ac.ic.wlgitbridge.application.config; -/** +/* * Created by winston on 25/10/15. */ public class Oauth2 { - private final String oauth2ClientID; - private final String oauth2ClientSecret; - private final String oauth2Server; + private final String oauth2ClientID; + private final String oauth2ClientSecret; + private final String oauth2Server; - public Oauth2( - String oauth2ClientID, - String oauth2ClientSecret, - String oauth2Server - ) { - this.oauth2ClientID = oauth2ClientID; - this.oauth2ClientSecret = oauth2ClientSecret; - this.oauth2Server = oauth2Server; - } + public Oauth2(String oauth2ClientID, String oauth2ClientSecret, String oauth2Server) { + this.oauth2ClientID = oauth2ClientID; + this.oauth2ClientSecret = oauth2ClientSecret; + this.oauth2Server = oauth2Server; + } - public String getOauth2ClientID() { - return oauth2ClientID; - } + public String getOauth2ClientID() { + return oauth2ClientID; + } - public String getOauth2ClientSecret() { - return oauth2ClientSecret; - } + public String getOauth2ClientSecret() { + return oauth2ClientSecret; + } - public String getOauth2Server() { - return oauth2Server; - } - - public static Oauth2 asSanitised(Oauth2 oauth2) { - return new Oauth2( - "", - "", - oauth2.oauth2Server - ); - } + public String getOauth2Server() { + return oauth2Server; + } + public static Oauth2 asSanitised(Oauth2 oauth2) { + return new Oauth2("", "", oauth2.oauth2Server); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ArgsException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ArgsException.java index 9557c35f29..5717aff89f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ArgsException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ArgsException.java @@ -1,6 +1,6 @@ package uk.ac.ic.wlgitbridge.application.exception; -/** +/* * Created by Winston on 03/11/14. */ public class ArgsException extends Exception {} diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ConfigFileException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ConfigFileException.java index bbe9671dc2..8e6ac7dbed 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ConfigFileException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/exception/ConfigFileException.java @@ -1,18 +1,17 @@ package uk.ac.ic.wlgitbridge.application.exception; -/** +/* * Created by Winston on 05/12/14. */ public class ConfigFileException extends Exception { - private final String missingMember; + private final String missingMember; - public ConfigFileException(String missingMember) { - this.missingMember = missingMember; - } - - public String getMissingMember() { - return missingMember; - } + public ConfigFileException(String missingMember) { + this.missingMember = missingMember; + } + public String getMissingMember() { + return missingMember; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/jetty/NullLogger.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/jetty/NullLogger.java index e996bd50c3..4543a59ee0 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/jetty/NullLogger.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/jetty/NullLogger.java @@ -2,84 +2,59 @@ package uk.ac.ic.wlgitbridge.application.jetty; import org.eclipse.jetty.util.log.Logger; -/** +/* * Created by Winston on 03/11/14. */ public class NullLogger implements Logger { - @Override - public String getName() { - return "null_logger"; - } + @Override + public String getName() { + return "null_logger"; + } - @Override - public void warn(String s, Object... objects) { + @Override + public void warn(String s, Object... objects) {} - } + @Override + public void warn(Throwable throwable) {} - @Override - public void warn(Throwable throwable) { + @Override + public void warn(String s, Throwable throwable) {} - } + @Override + public void info(String s, Object... objects) {} - @Override - public void warn(String s, Throwable throwable) { + @Override + public void info(Throwable throwable) {} - } + @Override + public void info(String s, Throwable throwable) {} - @Override - public void info(String s, Object... objects) { + @Override + public boolean isDebugEnabled() { + return false; + } - } + @Override + public void setDebugEnabled(boolean b) {} - @Override - public void info(Throwable throwable) { + @Override + public void debug(String s, Object... objects) {} - } + @Override + public void debug(String s, long l) {} - @Override - public void info(String s, Throwable throwable) { + @Override + public void debug(Throwable throwable) {} - } + @Override + public void debug(String s, Throwable throwable) {} - @Override - public boolean isDebugEnabled() { - return false; - } - - @Override - public void setDebugEnabled(boolean b) { - - } - - @Override - public void debug(String s, Object... objects) { - - } - - @Override - public void debug(String s, long l) { - - } - - @Override - public void debug(Throwable throwable) { - - } - - @Override - public void debug(String s, Throwable throwable) { - - } - - @Override - public Logger getLogger(String s) { - return this; - } - - @Override - public void ignore(Throwable throwable) { - - } + @Override + public Logger getLogger(String s) { + return this; + } + @Override + public void ignore(Throwable throwable) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/Bridge.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/Bridge.java index f448024e9a..83fbedadaf 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/Bridge.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/Bridge.java @@ -1,11 +1,15 @@ package uk.ac.ic.wlgitbridge.bridge; import com.google.api.client.auth.oauth2.Credential; +import java.io.File; +import java.io.IOException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.*; import org.eclipse.jgit.errors.RepositoryNotFoundException; import uk.ac.ic.wlgitbridge.application.config.Config; import uk.ac.ic.wlgitbridge.bridge.db.DBStore; import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SqliteDBStore; import uk.ac.ic.wlgitbridge.bridge.gc.GcJob; import uk.ac.ic.wlgitbridge.bridge.gc.GcJobImpl; import uk.ac.ic.wlgitbridge.bridge.lock.LockGuard; @@ -13,12 +17,9 @@ import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; import uk.ac.ic.wlgitbridge.bridge.repo.*; import uk.ac.ic.wlgitbridge.bridge.resource.ResourceCache; import uk.ac.ic.wlgitbridge.bridge.resource.UrlResourceCache; -import uk.ac.ic.wlgitbridge.bridge.snapshot.NetSnapshotApi; import uk.ac.ic.wlgitbridge.bridge.snapshot.SnapshotApi; import uk.ac.ic.wlgitbridge.bridge.snapshot.SnapshotApiFacade; import uk.ac.ic.wlgitbridge.bridge.swap.job.SwapJob; -import uk.ac.ic.wlgitbridge.bridge.swap.job.SwapJobImpl; -import uk.ac.ic.wlgitbridge.bridge.swap.store.S3SwapStore; import uk.ac.ic.wlgitbridge.bridge.swap.store.SwapStore; import uk.ac.ic.wlgitbridge.data.CandidateSnapshot; import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; @@ -27,33 +28,19 @@ import uk.ac.ic.wlgitbridge.data.filestore.GitDirectoryContents; import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; import uk.ac.ic.wlgitbridge.data.filestore.RawFile; import uk.ac.ic.wlgitbridge.data.model.Snapshot; +import uk.ac.ic.wlgitbridge.git.exception.FileLimitExceededException; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException; -import uk.ac.ic.wlgitbridge.git.exception.FileLimitExceededException; -import uk.ac.ic.wlgitbridge.git.handler.WLReceivePackFactory; -import uk.ac.ic.wlgitbridge.git.handler.WLRepositoryResolver; -import uk.ac.ic.wlgitbridge.git.handler.WLUploadPackFactory; -import uk.ac.ic.wlgitbridge.git.handler.hook.WriteLatexPutHook; -import uk.ac.ic.wlgitbridge.server.FileHandler; -import uk.ac.ic.wlgitbridge.server.PostbackContents; -import uk.ac.ic.wlgitbridge.server.PostbackHandler; -import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException; +import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotAttachment; import uk.ac.ic.wlgitbridge.snapshot.push.PostbackManager; -import uk.ac.ic.wlgitbridge.snapshot.push.PostbackPromise; import uk.ac.ic.wlgitbridge.snapshot.push.PushResult; import uk.ac.ic.wlgitbridge.snapshot.push.exception.*; import uk.ac.ic.wlgitbridge.util.Log; -import java.io.File; -import java.io.IOException; -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.util.*; - -/** +/* * This is the heart of the Git Bridge. You plug in all the parts (project * lock, repo store, db store, swap store, snapshot api, resource cache and * postback manager) is called by Git user requests and Overleaf postback @@ -66,7 +53,7 @@ import java.util.*; * @see WLRepositoryResolver - used on all requests associate a repo with a * project name, or fail * -* @see WLUploadPackFactory - used to handle clones and fetches + * @see WLUploadPackFactory - used to handle clones and fetches * * @see WLReceivePackFactory - used to handle pushes by setting a hook * @see WriteLatexPutHook - the hook used to handle pushes @@ -141,683 +128,582 @@ import java.util.*; */ public class Bridge { - private final Config config; + private final Config config; - private final ProjectLock lock; + private final ProjectLock lock; - private final RepoStore repoStore; - private final DBStore dbStore; - private final SwapStore swapStore; - private final SwapJob swapJob; - private final GcJob gcJob; + private final RepoStore repoStore; + private final DBStore dbStore; + private final SwapStore swapStore; + private final SwapJob swapJob; + private final GcJob gcJob; - private final SnapshotApiFacade snapshotAPI; - private final ResourceCache resourceCache; + private final SnapshotApiFacade snapshotAPI; + private final ResourceCache resourceCache; - private final PostbackManager postbackManager; + private final PostbackManager postbackManager; - /** - * Creates a Bridge from its configurable parts, which are the repo, db and - * swap store, and the swap job config. - * - * This should be the method used to create a Bridge. - * @param config The config to use - * @param repoStore The repo store to use - * @param dbStore The db store to use - * @param swapStore The swap store to use - * @param snapshotApi The snapshot api to use - * @return The constructed Bridge. - */ - public static Bridge make( - Config config, - RepoStore repoStore, - DBStore dbStore, - SwapStore swapStore, - SnapshotApi snapshotApi - ) { - ProjectLock lock = new ProjectLockImpl((int threads) -> - Log.info("Waiting for " + threads + " projects...") - ); - return new Bridge( - config, - lock, - repoStore, - dbStore, - swapStore, - SwapJob.fromConfig( - config.getSwapJob(), - lock, - repoStore, - dbStore, - swapStore - ), - new GcJobImpl(repoStore, lock), - new SnapshotApiFacade(snapshotApi), - new UrlResourceCache(dbStore) - ); + /* + * Creates a Bridge from its configurable parts, which are the repo, db and + * swap store, and the swap job config. + * + * This should be the method used to create a Bridge. + * @param config The config to use + * @param repoStore The repo store to use + * @param dbStore The db store to use + * @param swapStore The swap store to use + * @param snapshotApi The snapshot api to use + * @return The constructed Bridge. + */ + public static Bridge make( + Config config, + RepoStore repoStore, + DBStore dbStore, + SwapStore swapStore, + SnapshotApi snapshotApi) { + ProjectLock lock = + new ProjectLockImpl((int threads) -> Log.info("Waiting for " + threads + " projects...")); + return new Bridge( + config, + lock, + repoStore, + dbStore, + swapStore, + SwapJob.fromConfig(config.getSwapJob(), lock, repoStore, dbStore, swapStore), + new GcJobImpl(repoStore, lock), + new SnapshotApiFacade(snapshotApi), + new UrlResourceCache(dbStore)); + } + + /* + * Creates a bridge from all of its components, not just its configurable + * parts. This is for substituting mock/stub components for testing. + * It's also used by Bridge.make to actually construct the bridge. + * @param lock the {@link ProjectLock} to use + * @param repoStore the {@link RepoStore} to use + * @param dbStore the {@link DBStore} to use + * @param swapStore the {@link SwapStore} to use + * @param swapJob the {@link SwapJob} to use + * @param gcJob + * @param snapshotAPI the {@link SnapshotApi} to use + * @param resourceCache the {@link ResourceCache} to use + */ + Bridge( + Config config, + ProjectLock lock, + RepoStore repoStore, + DBStore dbStore, + SwapStore swapStore, + SwapJob swapJob, + GcJob gcJob, + SnapshotApiFacade snapshotAPI, + ResourceCache resourceCache) { + this.config = config; + this.lock = lock; + this.repoStore = repoStore; + this.dbStore = dbStore; + this.swapStore = swapStore; + this.snapshotAPI = snapshotAPI; + this.resourceCache = resourceCache; + this.swapJob = swapJob; + this.gcJob = gcJob; + postbackManager = new PostbackManager(); + Runtime.getRuntime().addShutdownHook(new Thread(this::doShutdown)); + repoStore.purgeNonexistentProjects(dbStore.getProjectNames()); + } + + /* + * This performs the graceful shutdown of the Bridge, which is called by the + * shutdown hook. It acquires the project write lock, which prevents + * work being done for new projects (which acquire the read lock). + * Once it has the write lock, there are no readers left, so the git bridge + * can shut down gracefully. + * + * It is also used by the tests. + */ + void doShutdown() { + Log.info("Shutdown received."); + Log.info("Stopping SwapJob"); + swapJob.stop(); + Log.info("Stopping GcJob"); + gcJob.stop(); + Log.info("Waiting for projects"); + lock.lockAll(); + Log.info("Bye"); + } + + /* + * Starts the swap job, which will begin checking whether projects should be + * swapped with a configurable frequency. + */ + public void startBackgroundJobs() { + swapJob.start(); + gcJob.start(); + } + + public boolean healthCheck() { + try { + dbStore.getNumProjects(); + File rootDirectory = new File("/"); + if (!rootDirectory.exists()) { + throw new Exception("bad filesystem state, root directory does not exist"); + } + Log.debug("[HealthCheck] passed"); + return true; + } catch (Exception e) { + Log.error("[HealthCheck] FAILED!", e); + return false; } + } - /** - * Creates a bridge from all of its components, not just its configurable - * parts. This is for substituting mock/stub components for testing. - * It's also used by Bridge.make to actually construct the bridge. - * @param lock the {@link ProjectLock} to use - * @param repoStore the {@link RepoStore} to use - * @param dbStore the {@link DBStore} to use - * @param swapStore the {@link SwapStore} to use - * @param swapJob the {@link SwapJob} to use - * @param gcJob - * @param snapshotAPI the {@link SnapshotApi} to use - * @param resourceCache the {@link ResourceCache} to use - */ - Bridge( - Config config, - ProjectLock lock, - RepoStore repoStore, - DBStore dbStore, - SwapStore swapStore, - SwapJob swapJob, - GcJob gcJob, - SnapshotApiFacade snapshotAPI, - ResourceCache resourceCache - ) { - this.config = config; - this.lock = lock; - this.repoStore = repoStore; - this.dbStore = dbStore; - this.swapStore = swapStore; - this.snapshotAPI = snapshotAPI; - this.resourceCache = resourceCache; - this.swapJob = swapJob; - this.gcJob = gcJob; - postbackManager = new PostbackManager(); - Runtime.getRuntime().addShutdownHook(new Thread(this::doShutdown)); - repoStore.purgeNonexistentProjects(dbStore.getProjectNames()); - } - - /** - * This performs the graceful shutdown of the Bridge, which is called by the - * shutdown hook. It acquires the project write lock, which prevents - * work being done for new projects (which acquire the read lock). - * Once it has the write lock, there are no readers left, so the git bridge - * can shut down gracefully. - * - * It is also used by the tests. - */ - void doShutdown() { - Log.info("Shutdown received."); - Log.info("Stopping SwapJob"); - swapJob.stop(); - Log.info("Stopping GcJob"); - gcJob.stop(); - Log.info("Waiting for projects"); - lock.lockAll(); - Log.info("Bye"); - } - - /** - * Starts the swap job, which will begin checking whether projects should be - * swapped with a configurable frequency. - */ - public void startBackgroundJobs() { - swapJob.start(); - gcJob.start(); - } - - public boolean healthCheck() { - try { - dbStore.getNumProjects(); - File rootDirectory = new File("/"); - if (!rootDirectory.exists()) { - throw new Exception("bad filesystem state, root directory does not exist"); - } - Log.debug("[HealthCheck] passed"); - return true; - } catch (Exception e) { - Log.error("[HealthCheck] FAILED!", e); - return false; - } - } - - /** - * Performs a check of inconsistencies in the DB. This was used to upgrade - * the schema. - */ - public void checkDB() { - Log.info("Checking DB"); - File rootDir = repoStore.getRootDirectory(); - for (File f : rootDir.listFiles()) { - if (f.getName().equals(".wlgb")) { - continue; - } - String projName = f.getName(); - try (LockGuard __ = lock.lockGuard(projName)) { - File dotGit = new File(f, ".git"); - if (!dotGit.exists()) { - Log.warn("Project: {} has no .git", projName); - continue; - } - ProjectState state = dbStore.getProjectState(projName); - if (state != ProjectState.NOT_PRESENT) { - continue; - } - Log.warn( - "Project: {} not in swap_store, adding", - projName - ); - dbStore.setLastAccessedTime( - projName, - new Timestamp(dotGit.lastModified()) - ); - } catch (CannotAcquireLockException e) { - throw new RuntimeException(e); - } + /* + * Performs a check of inconsistencies in the DB. This was used to upgrade + * the schema. + */ + public void checkDB() { + Log.info("Checking DB"); + File rootDir = repoStore.getRootDirectory(); + for (File f : rootDir.listFiles()) { + if (f.getName().equals(".wlgb")) { + continue; + } + String projName = f.getName(); + try (LockGuard __ = lock.lockGuard(projName)) { + File dotGit = new File(f, ".git"); + if (!dotGit.exists()) { + Log.warn("Project: {} has no .git", projName); + continue; } - } - - /** - * Synchronises the given repository with Overleaf. - * - * It acquires the project lock and calls - * {@link #getUpdatedRepoCritical(Optional, String, GetDocResult)}. - * @param oauth2 The oauth2 to use - * @param projectName The name of the project - * @throws IOException - * @throws GitUserException - */ - public ProjectRepo getUpdatedRepo( - Optional oauth2, - String projectName - ) throws IOException, GitUserException, CannotAcquireLockException { - try (LockGuard __ = lock.lockGuard(projectName)) { - Optional maybeDoc = snapshotAPI.getDoc(oauth2, projectName); - if (!maybeDoc.isPresent()) { - throw new RepositoryNotFoundException(projectName); - } - GetDocResult doc = maybeDoc.get(); - Log.debug("[{}] Updating repository", projectName); - return getUpdatedRepoCritical(oauth2, projectName, doc); + ProjectState state = dbStore.getProjectState(projName); + if (state != ProjectState.NOT_PRESENT) { + continue; } + Log.warn("Project: {} not in swap_store, adding", projName); + dbStore.setLastAccessedTime(projName, new Timestamp(dotGit.lastModified())); + } catch (CannotAcquireLockException e) { + throw new RuntimeException(e); + } + } + } + + /* + * Synchronises the given repository with Overleaf. + * + * It acquires the project lock and calls + * {@link #getUpdatedRepoCritical(Optional, String, GetDocResult)}. + * @param oauth2 The oauth2 to use + * @param projectName The name of the project + * @throws IOException + * @throws GitUserException + */ + public ProjectRepo getUpdatedRepo(Optional oauth2, String projectName) + throws IOException, GitUserException, CannotAcquireLockException { + try (LockGuard __ = lock.lockGuard(projectName)) { + Optional maybeDoc = snapshotAPI.getDoc(oauth2, projectName); + if (!maybeDoc.isPresent()) { + throw new RepositoryNotFoundException(projectName); + } + GetDocResult doc = maybeDoc.get(); + Log.debug("[{}] Updating repository", projectName); + return getUpdatedRepoCritical(oauth2, projectName, doc); + } + } + + /* + * Synchronises the given repository with Overleaf. + * + * Pre: the project lock must be acquired for the given repo. + * + * 1. Queries the project state for the given project name. + * a. NOT_PRESENT = We've never seen it before, and the row for the + * project doesn't even exist. The project definitely + * exists because we would have aborted otherwise. + * b. PRESENT = The project is on disk. + * c. SWAPPED = The project is in the {@link SwapStore} + * + * If the project has never been cloned, it is git init'd. If the project + * is in swap, it is restored to disk. Otherwise, the project was already + * present. + * + * With the project present, snapshots are downloaded from the snapshot + * API with {@link #updateProject(Optional, ProjectRepo)}. + * + * Then, the last accessed time of the project is set to the current time. + * This is to support the LRU of the swap store. + * @param oauth2 + * @param projectName The name of the project + * @throws IOException + * @throws GitUserException + */ + private ProjectRepo getUpdatedRepoCritical( + Optional oauth2, String projectName, GetDocResult doc) + throws IOException, GitUserException { + ProjectRepo repo; + ProjectState state = dbStore.getProjectState(projectName); + switch (state) { + case NOT_PRESENT: + Log.info("[{}] Repo not present", projectName); + repo = repoStore.initRepo(projectName); + break; + case SWAPPED: + swapJob.restore(projectName); + repo = repoStore.getExistingRepo(projectName); + break; + default: + repo = repoStore.getExistingRepo(projectName); + } + updateProject(oauth2, repo); + dbStore.setLastAccessedTime(projectName, Timestamp.valueOf(LocalDateTime.now())); + return repo; + } + + /* + * The public call to push a project. + * + * It acquires the lock and calls {@link #pushCritical( + * Optional, + * String, + * RawDirectory, + * RawDirectory + * )}, catching exceptions, logging, and rethrowing them. + * @param oauth2 The oauth2 to use for the snapshot API + * @param projectName The name of the project to push to + * @param directoryContents The new contents of the project + * @param oldDirectoryContents The old contents of the project + * @param hostname + * @throws SnapshotPostException + * @throws IOException + * @throws MissingRepositoryException + * @throws ForbiddenException + * @throws GitUserException + */ + public void push( + Optional oauth2, + String projectName, + RawDirectory directoryContents, + RawDirectory oldDirectoryContents, + String hostname) + throws SnapshotPostException, + IOException, + MissingRepositoryException, + ForbiddenException, + GitUserException, + CannotAcquireLockException { + Log.debug("[{}] pushing to Overleaf", projectName); + try (LockGuard __ = lock.lockGuard(projectName)) { + Log.info("[{}] got project lock", projectName); + pushCritical(oauth2, projectName, directoryContents, oldDirectoryContents); + } catch (SevereSnapshotPostException e) { + Log.warn("[" + projectName + "] Failed to put to Overleaf", e); + throw e; + } catch (SnapshotPostException e) { + /* Stack trace should be printed further up */ + Log.warn( + "[{}] Exception when waiting for postback: {}", + projectName, + e.getClass().getSimpleName()); + throw e; + } catch (IOException e) { + Log.warn("[{}] IOException on put: {}", projectName, e); + throw e; } - /** - * Synchronises the given repository with Overleaf. - * - * Pre: the project lock must be acquired for the given repo. - * - * 1. Queries the project state for the given project name. - * a. NOT_PRESENT = We've never seen it before, and the row for the - * project doesn't even exist. The project definitely - * exists because we would have aborted otherwise. - * b. PRESENT = The project is on disk. - * c. SWAPPED = The project is in the {@link SwapStore} - * - * If the project has never been cloned, it is git init'd. If the project - * is in swap, it is restored to disk. Otherwise, the project was already - * present. - * - * With the project present, snapshots are downloaded from the snapshot - * API with {@link #updateProject(Optional, ProjectRepo)}. - * - * Then, the last accessed time of the project is set to the current time. - * This is to support the LRU of the swap store. - * @param oauth2 - * @param projectName The name of the project - * @throws IOException - * @throws GitUserException - */ - private ProjectRepo getUpdatedRepoCritical( - Optional oauth2, - String projectName, - GetDocResult doc - ) throws IOException, GitUserException { - ProjectRepo repo; - ProjectState state = dbStore.getProjectState(projectName); - switch (state) { - case NOT_PRESENT: - Log.info("[{}] Repo not present", projectName); - repo = repoStore.initRepo(projectName); - break; - case SWAPPED: - swapJob.restore(projectName); - repo = repoStore.getExistingRepo(projectName); - break; - default: - repo = repoStore.getExistingRepo(projectName); - } - updateProject(oauth2, repo); - dbStore.setLastAccessedTime( - projectName, - Timestamp.valueOf(LocalDateTime.now()) - ); - return repo; + gcJob.queueForGc(projectName); + } + + /* + * Does the work of pushing to a project, assuming the project lock is held. + * The {@link WriteLatexPutHook} is the original caller, and when we return + * without throwing, the commit is committed. + * + * We start off by creating a postback key, which is given in the url when + * the Overleaf app tries to access the atts. + * + * Then creates a {@link CandidateSnapshot} from the old and new project + * contents. The + * {@link CandidateSnapshot} is created using + * {@link #createCandidateSnapshot(String, RawDirectory, RawDirectory)}, + * which creates the snapshot object and writes the push files to the + * atts directory, which is served by the {@link PostbackHandler}. + * The files are deleted at the end of a try-with-resources block. + * + * Then 3 things are used to make the push request to the snapshot API: + * 1. The oauth2 + * 2. The candidate snapshot + * 3. The postback key + * + * If the snapshot API reports this as not successful, we immediately throw + * an {@link OutOfDateException}, which goes back to the user. + * + * Otherwise, we wait (with a timeout) on a promise from the postback + * manager, which can throw back to the user. + * + * If this is successful, we approve the snapshot with + * {@link #approveSnapshot(int, CandidateSnapshot)}, which updates our side + * of the push: the latest version and the URL index store. + * + * Then, we set the last accessed time for the swap store. + * + * Finally, after we return, the push to the repo from the hook is + * successful and the repo gets updated. + * + * @param oauth2 + * @param projectName + * @param directoryContents + * @param oldDirectoryContents + * @throws IOException + * @throws MissingRepositoryException + * @throws ForbiddenException + * @throws SnapshotPostException + * @throws GitUserException + */ + private void pushCritical( + Optional oauth2, + String projectName, + RawDirectory directoryContents, + RawDirectory oldDirectoryContents) + throws IOException, + MissingRepositoryException, + ForbiddenException, + SnapshotPostException, + GitUserException { + Optional maxFileNum = config.getRepoStore().flatMap(RepoStoreConfig::getMaxFileNum); + if (maxFileNum.isPresent()) { + long maxFileNum_ = maxFileNum.get(); + if (directoryContents.getFileTable().size() > maxFileNum_) { + Log.warn( + "[{}] Too many files: {}/{}", + projectName, + directoryContents.getFileTable().size(), + maxFileNum_); + throw new FileLimitExceededException(directoryContents.getFileTable().size(), maxFileNum_); + } } - - /** - * The public call to push a project. - * - * It acquires the lock and calls {@link #pushCritical( - * Optional, - * String, - * RawDirectory, - * RawDirectory - * )}, catching exceptions, logging, and rethrowing them. - * @param oauth2 The oauth2 to use for the snapshot API - * @param projectName The name of the project to push to - * @param directoryContents The new contents of the project - * @param oldDirectoryContents The old contents of the project - * @param hostname - * @throws SnapshotPostException - * @throws IOException - * @throws MissingRepositoryException - * @throws ForbiddenException - * @throws GitUserException - */ - public void push( - Optional oauth2, - String projectName, - RawDirectory directoryContents, - RawDirectory oldDirectoryContents, - String hostname - ) throws SnapshotPostException, IOException, MissingRepositoryException, ForbiddenException, GitUserException, CannotAcquireLockException { - Log.debug("[{}] pushing to Overleaf", projectName); - try (LockGuard __ = lock.lockGuard(projectName)) { - Log.info("[{}] got project lock", projectName); - pushCritical( - oauth2, - projectName, - directoryContents, - oldDirectoryContents - ); - } catch (SevereSnapshotPostException e) { - Log.warn( - "[" + projectName + "] Failed to put to Overleaf", - e - ); - throw e; - } catch (SnapshotPostException e) { - /* Stack trace should be printed further up */ - Log.warn( - "[{}] Exception when waiting for postback: {}", - projectName, - e.getClass().getSimpleName() - ); - throw e; - } catch (IOException e) { - Log.warn("[{}] IOException on put: {}", projectName, e); - throw e; - } - - gcJob.queueForGc(projectName); + Log.debug( + "[{}] Pushing files ({} new, {} old)", + projectName, + directoryContents.getFileTable().size(), + oldDirectoryContents.getFileTable().size()); + String postbackKey = postbackManager.makeKeyForProject(projectName); + Log.debug("[{}] Created postback key: {}", projectName, postbackKey); + try (CandidateSnapshot candidate = + createCandidateSnapshot(projectName, directoryContents, oldDirectoryContents); ) { + Log.debug("[{}] Candidate snapshot created: {}", projectName, candidate); + PushResult result = snapshotAPI.push(oauth2, candidate, postbackKey); + if (result.wasSuccessful()) { + Log.debug("[{}] Push to Overleaf successful", projectName); + Log.debug("[{}] Waiting for postback...", projectName); + int versionID = postbackManager.waitForVersionIdOrThrow(projectName); + Log.debug("[{}] Got version ID for push: {}", projectName, versionID); + approveSnapshot(versionID, candidate); + Log.debug("[{}] Approved version ID: {}", projectName, versionID); + dbStore.setLastAccessedTime(projectName, Timestamp.valueOf(LocalDateTime.now())); + } else { + Log.warn("[{}] Went out of date while waiting for push", projectName); + throw new OutOfDateException(); + } } + } - /** - * Does the work of pushing to a project, assuming the project lock is held. - * The {@link WriteLatexPutHook} is the original caller, and when we return - * without throwing, the commit is committed. - * - * We start off by creating a postback key, which is given in the url when - * the Overleaf app tries to access the atts. - * - * Then creates a {@link CandidateSnapshot} from the old and new project - * contents. The - * {@link CandidateSnapshot} is created using - * {@link #createCandidateSnapshot(String, RawDirectory, RawDirectory)}, - * which creates the snapshot object and writes the push files to the - * atts directory, which is served by the {@link PostbackHandler}. - * The files are deleted at the end of a try-with-resources block. - * - * Then 3 things are used to make the push request to the snapshot API: - * 1. The oauth2 - * 2. The candidate snapshot - * 3. The postback key - * - * If the snapshot API reports this as not successful, we immediately throw - * an {@link OutOfDateException}, which goes back to the user. - * - * Otherwise, we wait (with a timeout) on a promise from the postback - * manager, which can throw back to the user. - * - * If this is successful, we approve the snapshot with - * {@link #approveSnapshot(int, CandidateSnapshot)}, which updates our side - * of the push: the latest version and the URL index store. - * - * Then, we set the last accessed time for the swap store. - * - * Finally, after we return, the push to the repo from the hook is - * successful and the repo gets updated. - * - * @param oauth2 - * @param projectName - * @param directoryContents - * @param oldDirectoryContents - * @throws IOException - * @throws MissingRepositoryException - * @throws ForbiddenException - * @throws SnapshotPostException - * @throws GitUserException - */ - private void pushCritical( - Optional oauth2, - String projectName, - RawDirectory directoryContents, - RawDirectory oldDirectoryContents - ) throws IOException, MissingRepositoryException, ForbiddenException, SnapshotPostException, GitUserException { - Optional maxFileNum = config - .getRepoStore() - .flatMap(RepoStoreConfig::getMaxFileNum); - if (maxFileNum.isPresent()) { - long maxFileNum_ = maxFileNum.get(); - if (directoryContents.getFileTable().size() > maxFileNum_) { - Log.warn("[{}] Too many files: {}/{}", projectName, directoryContents.getFileTable().size(), maxFileNum_); - throw new FileLimitExceededException(directoryContents.getFileTable().size(), maxFileNum_); + /* + * A public call that should originate from the {@link FileHandler}. + * + * The {@link FileHandler} serves atts to the Overleaf app during a push. + * The Overleaf app includes the postback key in the request, which was + * originally given on a push request. + * + * This method checks that the postback key matches, and throws if not. + * + * The FileHandler should not serve the file if this throws. + * @param projectName The project name that this key belongs to + * @param postbackKey The key + * @throws InvalidPostbackKeyException If the key doesn't match + */ + public void checkPostbackKey(String projectName, String postbackKey) + throws InvalidPostbackKeyException { + postbackManager.checkPostbackKey(projectName, postbackKey); + } + + /* + * A public call that originates from the postback thread + * {@link PostbackContents#processPostback()}, i.e. once the Overleaf app + * has fetched all the atts and has committed the push and is happy, it + * calls back here, fulfilling the promise that the push + * {@link #push(Optional, String, RawDirectory, RawDirectory, String)} + * is waiting on. + * + * The Overleaf app will have invented a new version for the push, which is + * passed to the promise for the original push request to update the app. + * @param projectName The name of the project being pushed to + * @param postbackKey The postback key being used + * @param versionID the new version id to use + * @throws UnexpectedPostbackException if the postback key is invalid + */ + public void postbackReceivedSuccessfully(String projectName, String postbackKey, int versionID) + throws UnexpectedPostbackException { + Log.debug( + "[{}]" + " Postback received by postback thread, version: {}", projectName, versionID); + postbackManager.postVersionIDForProject(projectName, versionID, postbackKey); + } + + /* + * As with {@link #postbackReceivedSuccessfully(String, String, int)}, + * but with an exception instead. + * + * This is based on the JSON body of the postback from the Overleaf app. + * + * The most likely problem is an {@link OutOfDateException}. + * @param projectName The name of the project + * @param postbackKey The postback key being used + * @param exception The exception encountered + * @throws UnexpectedPostbackException If the postback key is invalid + */ + public void postbackReceivedWithException( + String projectName, String postbackKey, SnapshotPostException exception) + throws UnexpectedPostbackException { + Log.warn("[{}] Postback received with exception", projectName); + postbackManager.postExceptionForProject(projectName, exception, postbackKey); + } + + /* + * Delete a project's data + */ + public void deleteProject(String projectName) { + Log.info("[{}] deleting project", projectName); + dbStore.deleteProject(projectName); + try { + repoStore.remove(projectName); + } catch (IOException e) { + Log.warn("Failed to delete repository for project {}: {}", projectName, e); + } + swapStore.remove(projectName); + } + + /* PRIVATE */ + + /* + * Called by {@link #getUpdatedRepoCritical(Optional, String)} + * + * Does the actual work of getting the snapshots for a project from the + * snapshot API and committing them to a repo. + * + * If any snapshots were found, sets the latest version for the project. + * + * @param oauth2 + * @param repo + * @throws IOException + * @throws GitUserException + */ + private void updateProject(Optional oauth2, ProjectRepo repo) + throws IOException, GitUserException { + String projectName = repo.getProjectName(); + int latestVersionId = dbStore.getLatestVersionForProject(projectName); + Deque snapshots = snapshotAPI.getSnapshots(oauth2, projectName, latestVersionId); + + makeCommitsFromSnapshots(repo, snapshots); + + // TODO: in case crashes around here, add an + // "updating_from_commit" column to the DB as a way to rollback the + // any failed partial updates before re-trying + // Also need to consider the empty state (a new git init'd repo being + // the rollback target) + if (!snapshots.isEmpty()) { + dbStore.setLatestVersionForProject(projectName, snapshots.getLast().getVersionID()); + } + } + + /* + * Called by {@link #updateProject(Optional, ProjectRepo)}. + * + * Performs the actual Git commits on the disk. + * + * Each commit adds files to the db store + * ({@link ResourceCache#get(String, String, String, Map, Map, Optional)}, + * and then removes any files that were deleted. + * @param repo The repository to commit to + * @param snapshots The snapshots to commit + * @throws IOException If an IOException occurred + * @throws SizeLimitExceededException If one of the files was too big. + */ + private void makeCommitsFromSnapshots(ProjectRepo repo, Collection snapshots) + throws IOException, GitUserException { + String name = repo.getProjectName(); + Optional maxSize = config.getRepoStore().flatMap(RepoStoreConfig::getMaxFileSize); + for (Snapshot snapshot : snapshots) { + RawDirectory directory = repo.getDirectory(); + Map fileTable = directory.getFileTable(); + List files = new ArrayList<>(); + files.addAll(snapshot.getSrcs()); + for (RawFile file : files) { + long size = file.size(); + /* Can't throw in ifPresent... */ + if (maxSize.isPresent()) { + long maxSize_ = maxSize.get(); + if (size >= maxSize_) { + throw new SizeLimitExceededException(Optional.of(file.getPath()), size, maxSize_); } } - Log.debug("[{}] Pushing files ({} new, {} old)", projectName, directoryContents.getFileTable().size(), oldDirectoryContents.getFileTable().size()); - String postbackKey = postbackManager.makeKeyForProject(projectName); - Log.debug( - "[{}] Created postback key: {}", - projectName, - postbackKey - ); - try ( - CandidateSnapshot candidate = createCandidateSnapshot( - projectName, - directoryContents, - oldDirectoryContents - ); - ) { - Log.debug( - "[{}] Candidate snapshot created: {}", - projectName, - candidate - ); - PushResult result - = snapshotAPI.push(oauth2, candidate, postbackKey); - if (result.wasSuccessful()) { - Log.debug( - "[{}] Push to Overleaf successful", - projectName - ); - Log.debug("[{}] Waiting for postback...", projectName); - int versionID = - postbackManager.waitForVersionIdOrThrow(projectName); - Log.debug( - "[{}] Got version ID for push: {}", - projectName, - versionID - ); - approveSnapshot(versionID, candidate); - Log.debug( - "[{}] Approved version ID: {}", - projectName, - versionID - ); - dbStore.setLastAccessedTime( - projectName, - Timestamp.valueOf(LocalDateTime.now()) - ); - } else { - Log.warn( - "[{}] Went out of date while waiting for push", - projectName - ); - throw new OutOfDateException(); - } - } - } - - /** - * A public call that should originate from the {@link FileHandler}. - * - * The {@link FileHandler} serves atts to the Overleaf app during a push. - * The Overleaf app includes the postback key in the request, which was - * originally given on a push request. - * - * This method checks that the postback key matches, and throws if not. - * - * The FileHandler should not serve the file if this throws. - * @param projectName The project name that this key belongs to - * @param postbackKey The key - * @throws InvalidPostbackKeyException If the key doesn't match - */ - public void checkPostbackKey(String projectName, String postbackKey) - throws InvalidPostbackKeyException { - postbackManager.checkPostbackKey(projectName, postbackKey); - } - - /** - * A public call that originates from the postback thread - * {@link PostbackContents#processPostback()}, i.e. once the Overleaf app - * has fetched all the atts and has committed the push and is happy, it - * calls back here, fulfilling the promise that the push - * {@link #push(Optional, String, RawDirectory, RawDirectory, String)} - * is waiting on. - * - * The Overleaf app will have invented a new version for the push, which is - * passed to the promise for the original push request to update the app. - * @param projectName The name of the project being pushed to - * @param postbackKey The postback key being used - * @param versionID the new version id to use - * @throws UnexpectedPostbackException if the postback key is invalid - */ - public void postbackReceivedSuccessfully( - String projectName, - String postbackKey, - int versionID - ) throws UnexpectedPostbackException { - Log.debug( - "[{}]" + - " Postback received by postback thread, version: {}", - projectName, - versionID); - postbackManager.postVersionIDForProject( - projectName, - versionID, - postbackKey - ); - } - - /** - * As with {@link #postbackReceivedSuccessfully(String, String, int)}, - * but with an exception instead. - * - * This is based on the JSON body of the postback from the Overleaf app. - * - * The most likely problem is an {@link OutOfDateException}. - * @param projectName The name of the project - * @param postbackKey The postback key being used - * @param exception The exception encountered - * @throws UnexpectedPostbackException If the postback key is invalid - */ - public void postbackReceivedWithException( - String projectName, - String postbackKey, - SnapshotPostException exception - ) throws UnexpectedPostbackException { - Log.warn("[{}] Postback received with exception", projectName); - postbackManager.postExceptionForProject( - projectName, - exception, - postbackKey - ); - } - - /** - * Delete a project's data - */ - public void deleteProject(String projectName) { - Log.info("[{}] deleting project", projectName); - dbStore.deleteProject(projectName); - try { - repoStore.remove(projectName); - } catch (IOException e) { - Log.warn("Failed to delete repository for project {}: {}", projectName, e); } - swapStore.remove(projectName); + Map fetchedUrls = new HashMap<>(); + for (SnapshotAttachment snapshotAttachment : snapshot.getAtts()) { + files.add( + resourceCache.get( + name, + snapshotAttachment.getUrl(), + snapshotAttachment.getPath(), + fileTable, + fetchedUrls, + maxSize)); + } + Log.debug("[{}] Committing version ID: {}", name, snapshot.getVersionID()); + Collection missingFiles = + repo.commitAndGetMissing( + new GitDirectoryContents(files, repoStore.getRootDirectory(), name, snapshot)); + dbStore.deleteFilesForProject(name, missingFiles.toArray(new String[missingFiles.size()])); } + } - /* PRIVATE */ - - /** - * Called by {@link #getUpdatedRepoCritical(Optional, String)} - * - * Does the actual work of getting the snapshots for a project from the - * snapshot API and committing them to a repo. - * - * If any snapshots were found, sets the latest version for the project. - * - * @param oauth2 - * @param repo - * @throws IOException - * @throws GitUserException - */ - private void updateProject( - Optional oauth2, - ProjectRepo repo - ) throws IOException, GitUserException { - String projectName = repo.getProjectName(); - int latestVersionId = dbStore.getLatestVersionForProject(projectName); - Deque snapshots = snapshotAPI.getSnapshots( - oauth2, projectName, latestVersionId); - - makeCommitsFromSnapshots(repo, snapshots); - - // TODO: in case crashes around here, add an - // "updating_from_commit" column to the DB as a way to rollback the - // any failed partial updates before re-trying - // Also need to consider the empty state (a new git init'd repo being - // the rollback target) - if (!snapshots.isEmpty()) { - dbStore.setLatestVersionForProject( - projectName, - snapshots.getLast().getVersionID() - ); - } - } - - /** - * Called by {@link #updateProject(Optional, ProjectRepo)}. - * - * Performs the actual Git commits on the disk. - * - * Each commit adds files to the db store - * ({@link ResourceCache#get(String, String, String, Map, Map, Optional)}, - * and then removes any files that were deleted. - * @param repo The repository to commit to - * @param snapshots The snapshots to commit - * @throws IOException If an IOException occurred - * @throws SizeLimitExceededException If one of the files was too big. - */ - private void makeCommitsFromSnapshots( - ProjectRepo repo, - Collection snapshots - ) throws IOException, GitUserException { - String name = repo.getProjectName(); - Optional maxSize = config - .getRepoStore() - .flatMap(RepoStoreConfig::getMaxFileSize); - for (Snapshot snapshot : snapshots) { - RawDirectory directory = repo.getDirectory(); - Map fileTable = directory.getFileTable(); - List files = new ArrayList<>(); - files.addAll(snapshot.getSrcs()); - for (RawFile file : files) { - long size = file.size(); - /* Can't throw in ifPresent... */ - if (maxSize.isPresent()) { - long maxSize_ = maxSize.get(); - if (size >= maxSize_) { - throw new SizeLimitExceededException( - Optional.of(file.getPath()), size, maxSize_); - } - } - } - Map fetchedUrls = new HashMap<>(); - for (SnapshotAttachment snapshotAttachment : snapshot.getAtts()) { - files.add( - resourceCache.get( - name, - snapshotAttachment.getUrl(), - snapshotAttachment.getPath(), - fileTable, - fetchedUrls, - maxSize - ) - ); - } - Log.debug( - "[{}] Committing version ID: {}", - name, - snapshot.getVersionID() - ); - Collection missingFiles = repo.commitAndGetMissing( - new GitDirectoryContents( - files, - repoStore.getRootDirectory(), - name, - snapshot - ) - ); - dbStore.deleteFilesForProject( - name, - missingFiles.toArray(new String[missingFiles.size()]) - ); - } - } - - /** - * Called by - * {@link #pushCritical(Optional, String, RawDirectory, RawDirectory)}. - * - * This call consists of 2 things: Creating the candidate snapshot, - * and writing the atts to the atts directory. - * - * The candidate snapshot RAIIs away those atts (use try-with-resources). - * @param projectName The name of the project - * @param directoryContents The new directory contents - * @param oldDirectoryContents The old directory contents - * @return The {@link CandidateSnapshot} created - * @throws IOException If an I/O exception occurred on writing - */ - private CandidateSnapshot createCandidateSnapshot( - String projectName, - RawDirectory directoryContents, - RawDirectory oldDirectoryContents - ) throws IOException { - CandidateSnapshot candidateSnapshot = new CandidateSnapshot( - projectName, - dbStore.getLatestVersionForProject(projectName), - directoryContents, - oldDirectoryContents - ); - candidateSnapshot.writeServletFiles(repoStore.getRootDirectory()); - return candidateSnapshot; - } - - /** - * Called by - * {@link #pushCritical(Optional, String, RawDirectory, RawDirectory)}. - * - * This method approves a push by setting the latest version and removing - * any deleted files from the db store (files were already added by the - * resources cache). - * @param versionID - * @param candidateSnapshot - */ - private void approveSnapshot( - int versionID, - CandidateSnapshot candidateSnapshot - ) { - List deleted = candidateSnapshot.getDeleted(); - dbStore.setLatestVersionForProject( - candidateSnapshot.getProjectName(), - versionID - ); - dbStore.deleteFilesForProject( - candidateSnapshot.getProjectName(), - deleted.toArray(new String[deleted.size()]) - ); - } + /* + * Called by + * {@link #pushCritical(Optional, String, RawDirectory, RawDirectory)}. + * + * This call consists of 2 things: Creating the candidate snapshot, + * and writing the atts to the atts directory. + * + * The candidate snapshot RAIIs away those atts (use try-with-resources). + * @param projectName The name of the project + * @param directoryContents The new directory contents + * @param oldDirectoryContents The old directory contents + * @return The {@link CandidateSnapshot} created + * @throws IOException If an I/O exception occurred on writing + */ + private CandidateSnapshot createCandidateSnapshot( + String projectName, RawDirectory directoryContents, RawDirectory oldDirectoryContents) + throws IOException { + CandidateSnapshot candidateSnapshot = + new CandidateSnapshot( + projectName, + dbStore.getLatestVersionForProject(projectName), + directoryContents, + oldDirectoryContents); + candidateSnapshot.writeServletFiles(repoStore.getRootDirectory()); + return candidateSnapshot; + } + /* + * Called by + * {@link #pushCritical(Optional, String, RawDirectory, RawDirectory)}. + * + * This method approves a push by setting the latest version and removing + * any deleted files from the db store (files were already added by the + * resources cache). + * @param versionID + * @param candidateSnapshot + */ + private void approveSnapshot(int versionID, CandidateSnapshot candidateSnapshot) { + List deleted = candidateSnapshot.getDeleted(); + dbStore.setLatestVersionForProject(candidateSnapshot.getProjectName(), versionID); + dbStore.deleteFilesForProject( + candidateSnapshot.getProjectName(), deleted.toArray(new String[deleted.size()])); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBInitException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBInitException.java index 0c8a6d7b04..633ae5195e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBInitException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBInitException.java @@ -1,20 +1,19 @@ package uk.ac.ic.wlgitbridge.bridge.db; -/** +/* * Created by winston on 23/08/2016. */ public class DBInitException extends RuntimeException { - public DBInitException(String message) { - super(message); - } + public DBInitException(String message) { + super(message); + } - public DBInitException(String message, Throwable cause) { - super(message, cause); - } - - public DBInitException(Throwable cause) { - super(cause); - } + public DBInitException(String message, Throwable cause) { + super(message, cause); + } + public DBInitException(Throwable cause) { + super(cause); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBStore.java index 9b7f8eebd6..01f37773ca 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/DBStore.java @@ -3,46 +3,46 @@ package uk.ac.ic.wlgitbridge.bridge.db; import java.sql.Timestamp; import java.util.List; -/** +/* * Created by winston on 20/08/2016. */ public interface DBStore { - int getNumProjects(); + int getNumProjects(); - List getProjectNames(); + List getProjectNames(); - void setLatestVersionForProject(String project, int versionID); + void setLatestVersionForProject(String project, int versionID); - int getLatestVersionForProject(String project); + int getLatestVersionForProject(String project); - void addURLIndexForProject(String projectName, String url, String path); + void addURLIndexForProject(String projectName, String url, String path); - void deleteFilesForProject(String project, String... files); + void deleteFilesForProject(String project, String... files); - String getPathForURLInProject(String projectName, String url); + String getPathForURLInProject(String projectName, String url); - String getOldestUnswappedProject(); + String getOldestUnswappedProject(); - void swap(String projectName, String compressionMethod); + void swap(String projectName, String compressionMethod); - void restore(String projectName); + void restore(String projectName); - String getSwapCompression(String projectName); + String getSwapCompression(String projectName); - int getNumUnswappedProjects(); + int getNumUnswappedProjects(); - ProjectState getProjectState(String projectName); + ProjectState getProjectState(String projectName); - /** - * Sets the last accessed time for the given project name. - * @param projectName the project's name - * @param time the time, or null if the project is to be swapped - */ - void setLastAccessedTime(String projectName, Timestamp time); + /* + * Sets the last accessed time for the given project name. + * @param projectName the project's name + * @param time the time, or null if the project is to be swapped + */ + void setLastAccessedTime(String projectName, Timestamp time); - /** - * Delete the metadata associated with the given project. - */ - void deleteProject(String projectName); + /* + * Delete the metadata associated with the given project. + */ + void deleteProject(String projectName); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/ProjectState.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/ProjectState.java index ad389f5ac1..3e85116cee 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/ProjectState.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/ProjectState.java @@ -1,12 +1,10 @@ package uk.ac.ic.wlgitbridge.bridge.db; -/** +/* * Created by winston on 24/08/2016. */ public enum ProjectState { - - NOT_PRESENT, - PRESENT, - SWAPPED - + NOT_PRESENT, + PRESENT, + SWAPPED } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/noop/NoopDbStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/noop/NoopDbStore.java index 15846f28a4..20d42022a2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/noop/NoopDbStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/noop/NoopDbStore.java @@ -1,78 +1,70 @@ package uk.ac.ic.wlgitbridge.bridge.db.noop; -import uk.ac.ic.wlgitbridge.bridge.db.DBStore; -import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; - import java.sql.Timestamp; import java.util.List; +import uk.ac.ic.wlgitbridge.bridge.db.DBStore; +import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; public class NoopDbStore implements DBStore { - @Override - public int getNumProjects() { - return 0; - } + @Override + public int getNumProjects() { + return 0; + } - @Override - public List getProjectNames() { - return null; - } + @Override + public List getProjectNames() { + return null; + } - @Override - public void setLatestVersionForProject(String project, int versionID) { + @Override + public void setLatestVersionForProject(String project, int versionID) {} - } + @Override + public int getLatestVersionForProject(String project) { + return 0; + } - @Override - public int getLatestVersionForProject(String project) { - return 0; - } + @Override + public void addURLIndexForProject(String projectName, String url, String path) {} - @Override - public void addURLIndexForProject(String projectName, String url, String path) { + @Override + public void deleteFilesForProject(String project, String... files) {} - } + @Override + public String getPathForURLInProject(String projectName, String url) { + return null; + } - @Override - public void deleteFilesForProject(String project, String... files) { + @Override + public String getOldestUnswappedProject() { + return null; + } - } + @Override + public int getNumUnswappedProjects() { + return 0; + } - @Override - public String getPathForURLInProject(String projectName, String url) { - return null; - } + @Override + public ProjectState getProjectState(String projectName) { + return null; + } - @Override - public String getOldestUnswappedProject() { - return null; - } + @Override + public void setLastAccessedTime(String projectName, Timestamp time) {} - @Override - public int getNumUnswappedProjects() { - return 0; - } + @Override + public void swap(String projectName, String compressionMethod) {} - @Override - public ProjectState getProjectState(String projectName) { - return null; - } + @Override + public void restore(String projectName) {} - @Override - public void setLastAccessedTime(String projectName, Timestamp time) { - } + @Override + public String getSwapCompression(String projectName) { + return null; + } - @Override - public void swap(String projectName, String compressionMethod) {} - - @Override - public void restore(String projectName) {} - - @Override - public String getSwapCompression(String projectName) { - return null; - } - - @Override - public void deleteProject(String projectName) {} + @Override + public void deleteProject(String projectName) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLQuery.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLQuery.java index 0873d133b8..faa6a3ff11 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLQuery.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLQuery.java @@ -3,11 +3,10 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite; import java.sql.ResultSet; import java.sql.SQLException; -/** +/* * Created by Winston on 20/11/14. */ public interface SQLQuery extends SQLUpdate { - public T processResultSet(ResultSet resultSet) throws SQLException; - + public T processResultSet(ResultSet resultSet) throws SQLException; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLUpdate.java index 167be212c0..9e79fdcfc8 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SQLUpdate.java @@ -3,16 +3,12 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite; import java.sql.PreparedStatement; import java.sql.SQLException; -/** +/* * Created by Winston on 20/11/14. */ public interface SQLUpdate { - String getSQL(); - default void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - - } + String getSQL(); + default void addParametersToStatement(PreparedStatement statement) throws SQLException {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStore.java index 1699915da9..6647a5984b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStore.java @@ -1,6 +1,10 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite; import com.google.common.base.Preconditions; +import java.io.File; +import java.sql.*; +import java.util.List; +import java.util.stream.Stream; import uk.ac.ic.wlgitbridge.bridge.db.DBInitException; import uk.ac.ic.wlgitbridge.bridge.db.DBStore; import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; @@ -10,225 +14,214 @@ import uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.create.*; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.delete.*; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.insert.*; -import java.io.File; -import java.sql.*; -import java.util.List; -import java.util.stream.Stream; - -/** +/* * Created by Winston on 17/11/14. */ public class SqliteDBStore implements DBStore { - private final Connection connection; - private int heapLimitBytes = 0; + private final Connection connection; + private int heapLimitBytes = 0; - public SqliteDBStore(File dbFile) { - this(dbFile, 0); + public SqliteDBStore(File dbFile) { + this(dbFile, 0); + } + + public SqliteDBStore(File dbFile, int heapLimitBytes) { + this.heapLimitBytes = heapLimitBytes; + try { + connection = openConnectionTo(dbFile); + createTables(); + } catch (Throwable t) { + throw new DBInitException(t); + } + } + + @Override + public int getNumProjects() { + return query(new GetNumProjects()); + } + + @Override + public List getProjectNames() { + return query(new GetProjectNamesSQLQuery()); + } + + @Override + public void setLatestVersionForProject(String projectName, int versionID) { + update(new SetProjectSQLUpdate(projectName, versionID)); + } + + @Override + public int getLatestVersionForProject(String projectName) { + return query(new GetLatestVersionForProjectSQLQuery(projectName)); + } + + @Override + public void addURLIndexForProject(String projectName, String url, String path) { + update(new AddURLIndexSQLUpdate(projectName, url, path)); + } + + @Override + public void deleteFilesForProject(String projectName, String... paths) { + update(new DeleteFilesForProjectSQLUpdate(projectName, paths)); + } + + @Override + public String getPathForURLInProject(String projectName, String url) { + return query(new GetPathForURLInProjectSQLQuery(projectName, url)); + } + + @Override + public String getOldestUnswappedProject() { + return query(new GetOldestProjectName()); + } + + @Override + public int getNumUnswappedProjects() { + return query(new GetNumUnswappedProjects()); + } + + @Override + public ProjectState getProjectState(String projectName) { + return query(new GetProjectState(projectName)); + } + + @Override + public void setLastAccessedTime(String projectName, Timestamp lastAccessed) { + update(new SetProjectLastAccessedTime(projectName, lastAccessed)); + } + + @Override + public void swap(String projectName, String compressionMethod) { + update(new UpdateSwap(projectName, compressionMethod)); + } + + @Override + public void restore(String projectName) { + update(new UpdateRestore(projectName)); + } + + @Override + public String getSwapCompression(String projectName) { + return query(new GetSwapCompression(projectName)); + } + + @Override + public void deleteProject(String projectName) { + update(new DeleteAllFilesInProjectSQLUpdate(projectName)); + update(new DeleteProjectSQLUpdate(projectName)); + } + + private Connection openConnectionTo(File dbFile) { + File parentDir = dbFile.getParentFile(); + if (!parentDir.exists() && !parentDir.mkdirs()) { + throw new DBInitException( + parentDir.getAbsolutePath() + + " directory didn't exist, " + + "and unable to create. Check your permissions."); + } + try { + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException e) { + throw new DBInitException(e); + } + try { + return DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath()); + } catch (SQLException e) { + throw new DBInitException("Unable to connect to DB", e); + } + } + + private void createTables() { + /* Migrations */ + /* We need to eat exceptions from here */ + try { + doUpdate(new SetSoftHeapLimitPragma(this.heapLimitBytes)); + } catch (SQLException ignore) { + } + try { + doUpdate(new ProjectsAddLastAccessed()); + } catch (SQLException ignore) { + } + try { + doUpdate(new ProjectsAddSwapTime()); + } catch (SQLException ignore) { + } + try { + doUpdate(new ProjectsAddRestoreTime()); + } catch (SQLException ignore) { + } + try { + doUpdate(new ProjectsAddSwapCompression()); + } catch (SQLException ignore) { } - public SqliteDBStore(File dbFile, int heapLimitBytes) { - this.heapLimitBytes = heapLimitBytes; - try { - connection = openConnectionTo(dbFile); - createTables(); - } catch (Throwable t) { - throw new DBInitException(t); - } + /* Create tables (if they don't exist) */ + Stream.of( + new CreateProjectsTableSQLUpdate(), + new CreateProjectsIndexLastAccessed(), + new CreateURLIndexStoreSQLUpdate(), + new CreateIndexURLIndexStore()) + .forEach(this::update); + + /* In the case of needing to change the schema, we need to check that + migrations didn't just fail */ + Preconditions.checkState(query(new LastAccessedColumnExists())); + Preconditions.checkState(query(new SwapTimeColumnExists())); + Preconditions.checkState(query(new RestoreTimeColumnExists())); + Preconditions.checkState(query(new SwapCompressionColumnExists())); + } + + private void update(SQLUpdate update) { + try { + doUpdate(update); + } catch (SQLException e) { + throw new RuntimeException(e); } + } - @Override - public int getNumProjects() { - return query(new GetNumProjects()); + private T query(SQLQuery query) { + try { + return doQuery(query); + } catch (SQLException e) { + throw new RuntimeException(e); } + } - @Override - public List getProjectNames() { - return query(new GetProjectNamesSQLQuery()); + private void doUpdate(SQLUpdate update) throws SQLException { + PreparedStatement statement = null; + try { + statement = connection.prepareStatement(update.getSQL()); + update.addParametersToStatement(statement); + statement.executeUpdate(); + } catch (SQLException e) { + throw e; + } finally { + try { + statement.close(); + } catch (Throwable t) { + throw new SQLException(t); + } } + } - @Override - public void setLatestVersionForProject( - String projectName, - int versionID - ) { - update(new SetProjectSQLUpdate(projectName, versionID)); + private T doQuery(SQLQuery query) throws SQLException { + PreparedStatement statement = null; + ResultSet results = null; + try { + statement = connection.prepareStatement(query.getSQL()); + query.addParametersToStatement(statement); + results = statement.executeQuery(); + return query.processResultSet(results); + } catch (SQLException e) { + throw e; + } finally { + if (statement != null) { + statement.close(); + } + if (results != null) { + results.close(); + } } - - @Override - public int getLatestVersionForProject( - String projectName - ) { - return query(new GetLatestVersionForProjectSQLQuery(projectName)); - } - - @Override - public void addURLIndexForProject( - String projectName, - String url, - String path - ) { - update(new AddURLIndexSQLUpdate(projectName, url, path)); - } - - @Override - public void deleteFilesForProject( - String projectName, - String... paths - ) { - update(new DeleteFilesForProjectSQLUpdate(projectName, paths)); - } - - @Override - public String getPathForURLInProject( - String projectName, - String url - ) { - return query(new GetPathForURLInProjectSQLQuery(projectName, url)); - } - - @Override - public String getOldestUnswappedProject() { - return query(new GetOldestProjectName()); - } - - @Override - public int getNumUnswappedProjects() { - return query(new GetNumUnswappedProjects()); - } - - @Override - public ProjectState getProjectState(String projectName) { - return query(new GetProjectState(projectName)); - } - - @Override - public void setLastAccessedTime( - String projectName, - Timestamp lastAccessed - ) { - update(new SetProjectLastAccessedTime(projectName, lastAccessed)); - } - - @Override - public void swap(String projectName, String compressionMethod) { - update(new UpdateSwap(projectName, compressionMethod)); - } - - @Override - public void restore(String projectName) { - update(new UpdateRestore(projectName)); - } - - @Override - public String getSwapCompression(String projectName) { - return query(new GetSwapCompression(projectName)); - } - - @Override - public void deleteProject(String projectName) { - update(new DeleteAllFilesInProjectSQLUpdate(projectName)); - update(new DeleteProjectSQLUpdate(projectName)); - } - - private Connection openConnectionTo(File dbFile) { - File parentDir = dbFile.getParentFile(); - if (!parentDir.exists() && !parentDir.mkdirs()) { - throw new DBInitException( - parentDir.getAbsolutePath() + " directory didn't exist, " + - "and unable to create. Check your permissions." - ); - } - try { - Class.forName("org.sqlite.JDBC"); - } catch (ClassNotFoundException e) { - throw new DBInitException(e); - } - try { - return DriverManager.getConnection( - "jdbc:sqlite:" + dbFile.getAbsolutePath() - ); - } catch (SQLException e) { - throw new DBInitException("Unable to connect to DB", e); - } - } - - private void createTables() { - /* Migrations */ - /* We need to eat exceptions from here */ - try { doUpdate(new SetSoftHeapLimitPragma(this.heapLimitBytes)); } catch (SQLException ignore) {} - try { doUpdate(new ProjectsAddLastAccessed()); } catch (SQLException ignore) {} - try { doUpdate(new ProjectsAddSwapTime()); } catch (SQLException ignore) {} - try { doUpdate(new ProjectsAddRestoreTime()); } catch (SQLException ignore) {} - try { doUpdate(new ProjectsAddSwapCompression()); } catch (SQLException ignore) {} - - /* Create tables (if they don't exist) */ - Stream.of( - new CreateProjectsTableSQLUpdate(), - new CreateProjectsIndexLastAccessed(), - new CreateURLIndexStoreSQLUpdate(), - new CreateIndexURLIndexStore() - ).forEach(this::update); - - /* In the case of needing to change the schema, we need to check that - migrations didn't just fail */ - Preconditions.checkState(query(new LastAccessedColumnExists())); - Preconditions.checkState(query(new SwapTimeColumnExists())); - Preconditions.checkState(query(new RestoreTimeColumnExists())); - Preconditions.checkState(query(new SwapCompressionColumnExists())); - } - - private void update(SQLUpdate update) { - try { - doUpdate(update); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - private T query(SQLQuery query) { - try { - return doQuery(query); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - private void doUpdate(SQLUpdate update) throws SQLException { - PreparedStatement statement = null; - try { - statement = connection.prepareStatement(update.getSQL()); - update.addParametersToStatement(statement); - statement.executeUpdate(); - } catch (SQLException e) { - throw e; - } finally { - try { - statement.close(); - } catch (Throwable t) { - throw new SQLException(t); - } - } - } - - private T doQuery(SQLQuery query) throws SQLException { - PreparedStatement statement = null; - ResultSet results = null; - try { - statement = connection.prepareStatement(query.getSQL()); - query.addParametersToStatement(statement); - results = statement.executeQuery(); - return query.processResultSet(results); - } catch (SQLException e) { - throw e; - } finally { - if (statement != null) { - statement.close(); - } - if (results != null) { - results.close(); - } - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetLatestVersionForProjectSQLQuery.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetLatestVersionForProjectSQLQuery.java index 4633fcf7cc..cc74e771f1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetLatestVersionForProjectSQLQuery.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetLatestVersionForProjectSQLQuery.java @@ -1,43 +1,40 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by Winston on 20/11/14. */ public class GetLatestVersionForProjectSQLQuery implements SQLQuery { - private static final String GET_VERSION_IDS_FOR_PROJECT_NAME = - "SELECT `version_id` FROM `projects` WHERE `name` = ?"; + private static final String GET_VERSION_IDS_FOR_PROJECT_NAME = + "SELECT `version_id` FROM `projects` WHERE `name` = ?"; - private final String projectName; + private final String projectName; - public GetLatestVersionForProjectSQLQuery(String projectName) { - this.projectName = projectName; + public GetLatestVersionForProjectSQLQuery(String projectName) { + this.projectName = projectName; + } + + @Override + public Integer processResultSet(ResultSet resultSet) throws SQLException { + int versionID = 0; + while (resultSet.next()) { + versionID = resultSet.getInt("version_id"); } + return versionID; + } - @Override - public Integer processResultSet(ResultSet resultSet) throws SQLException { - int versionID = 0; - while (resultSet.next()) { - versionID = resultSet.getInt("version_id"); - } - return versionID; - } + @Override + public String getSQL() { + return GET_VERSION_IDS_FOR_PROJECT_NAME; + } - @Override - public String getSQL() { - return GET_VERSION_IDS_FOR_PROJECT_NAME; - } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setString(1, projectName); - } + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setString(1, projectName); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumProjects.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumProjects.java index e5123c2ea1..30e00be5cb 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumProjects.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumProjects.java @@ -1,30 +1,26 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by winston on 24/08/2016. */ public class GetNumProjects implements SQLQuery { - private static final String GET_NUM_PROJECTS = - "SELECT COUNT(*)\n" + - " FROM `projects`"; + private static final String GET_NUM_PROJECTS = "SELECT COUNT(*)\n" + " FROM `projects`"; - @Override - public String getSQL() { - return GET_NUM_PROJECTS; + @Override + public String getSQL() { + return GET_NUM_PROJECTS; + } + + @Override + public Integer processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + return resultSet.getInt("COUNT(*)"); } - - @Override - public Integer processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - return resultSet.getInt("COUNT(*)"); - } - throw new IllegalStateException("Count always returns results"); - } - + throw new IllegalStateException("Count always returns results"); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumUnswappedProjects.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumUnswappedProjects.java index ec7a89b980..512f15cc53 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumUnswappedProjects.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetNumUnswappedProjects.java @@ -1,31 +1,27 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by winston on 24/08/2016. */ public class GetNumUnswappedProjects implements SQLQuery { - private static final String GET_NUM_UNSWAPPED_PROJECTS = - "SELECT COUNT(*)\n" + - " FROM `projects`\n" + - " WHERE `last_accessed` IS NOT NULL"; + private static final String GET_NUM_UNSWAPPED_PROJECTS = + "SELECT COUNT(*)\n" + " FROM `projects`\n" + " WHERE `last_accessed` IS NOT NULL"; - @Override - public String getSQL() { - return GET_NUM_UNSWAPPED_PROJECTS; + @Override + public String getSQL() { + return GET_NUM_UNSWAPPED_PROJECTS; + } + + @Override + public Integer processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + return resultSet.getInt("COUNT(*)"); } - - @Override - public Integer processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - return resultSet.getInt("COUNT(*)"); - } - throw new IllegalStateException("Count always returns results"); - } - + throw new IllegalStateException("Count always returns results"); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetOldestProjectName.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetOldestProjectName.java index 1128a53c01..3518977365 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetOldestProjectName.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetOldestProjectName.java @@ -1,31 +1,29 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by winston on 23/08/2016. */ public class GetOldestProjectName implements SQLQuery { - private static final String GET_OLDEST_PROJECT_NAME = - "SELECT `name`, MIN(`last_accessed`)\n" + - " FROM `projects` \n" + - " WHERE `last_accessed` IS NOT NULL;"; + private static final String GET_OLDEST_PROJECT_NAME = + "SELECT `name`, MIN(`last_accessed`)\n" + + " FROM `projects` \n" + + " WHERE `last_accessed` IS NOT NULL;"; - @Override - public String getSQL() { - return GET_OLDEST_PROJECT_NAME; + @Override + public String getSQL() { + return GET_OLDEST_PROJECT_NAME; + } + + @Override + public String processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + return resultSet.getString("name"); } - - @Override - public String processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - return resultSet.getString("name"); - } - return null; - } - + return null; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetPathForURLInProjectSQLQuery.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetPathForURLInProjectSQLQuery.java index b4905c7a8e..25beb18ad0 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetPathForURLInProjectSQLQuery.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetPathForURLInProjectSQLQuery.java @@ -1,50 +1,43 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by Winston on 20/11/14. */ public class GetPathForURLInProjectSQLQuery implements SQLQuery { - private static final String GET_URL_INDEXES_FOR_PROJECT_NAME = - "SELECT `path` " - + "FROM `url_index_store` " - + "WHERE `project_name` = ? " - + "AND `url` = ?"; + private static final String GET_URL_INDEXES_FOR_PROJECT_NAME = + "SELECT `path` " + "FROM `url_index_store` " + "WHERE `project_name` = ? " + "AND `url` = ?"; - private final String projectName; - private final String url; + private final String projectName; + private final String url; - public GetPathForURLInProjectSQLQuery(String projectName, String url) { - this.projectName = projectName; - this.url = url; + public GetPathForURLInProjectSQLQuery(String projectName, String url) { + this.projectName = projectName; + this.url = url; + } + + @Override + public String processResultSet(ResultSet resultSet) throws SQLException { + String path = null; + while (resultSet.next()) { + path = resultSet.getString("path"); } + return path; + } - @Override - public String processResultSet(ResultSet resultSet) throws SQLException { - String path = null; - while (resultSet.next()) { - path = resultSet.getString("path"); - } - return path; - } - - @Override - public String getSQL() { - return GET_URL_INDEXES_FOR_PROJECT_NAME; - } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setString(1, projectName); - statement.setString(2, url); - } + @Override + public String getSQL() { + return GET_URL_INDEXES_FOR_PROJECT_NAME; + } + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setString(1, projectName); + statement.setString(2, url); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectNamesSQLQuery.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectNamesSQLQuery.java index fcf0f761b9..d56ad5c70e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectNamesSQLQuery.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectNamesSQLQuery.java @@ -1,34 +1,29 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by Winston on 21/02/15. */ public class GetProjectNamesSQLQuery implements SQLQuery> { - private static final String GET_URL_INDEXES_FOR_PROJECT_NAME = - "SELECT `name` FROM `projects`"; + private static final String GET_URL_INDEXES_FOR_PROJECT_NAME = "SELECT `name` FROM `projects`"; - @Override - public List processResultSet( - ResultSet resultSet - ) throws SQLException { - List projectNames = new ArrayList<>(); - while (resultSet.next()) { - projectNames.add(resultSet.getString("name")); - } - return projectNames; - } - - @Override - public String getSQL() { - return GET_URL_INDEXES_FOR_PROJECT_NAME; + @Override + public List processResultSet(ResultSet resultSet) throws SQLException { + List projectNames = new ArrayList<>(); + while (resultSet.next()) { + projectNames.add(resultSet.getString("name")); } + return projectNames; + } + @Override + public String getSQL() { + return GET_URL_INDEXES_FOR_PROJECT_NAME; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectState.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectState.java index 0120289e89..a291695818 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectState.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetProjectState.java @@ -1,51 +1,43 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by winston on 24/08/2016. */ public class GetProjectState implements SQLQuery { - private static final String GET_PROJECT_STATE = - "SELECT `last_accessed`\n" + - " FROM `projects`\n" + - " WHERE `name` = ?"; + private static final String GET_PROJECT_STATE = + "SELECT `last_accessed`\n" + " FROM `projects`\n" + " WHERE `name` = ?"; - private final String projectName; + private final String projectName; - public GetProjectState(String projectName) { - this.projectName = projectName; - } - - @Override - public String getSQL() { - return GET_PROJECT_STATE; - } - - @Override - public ProjectState processResultSet( - ResultSet resultSet - ) throws SQLException { - while (resultSet.next()) { - if (resultSet.getTimestamp("last_accessed") == null) { - return ProjectState.SWAPPED; - } - return ProjectState.PRESENT; - } - return ProjectState.NOT_PRESENT; - } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setString(1, projectName); + public GetProjectState(String projectName) { + this.projectName = projectName; + } + + @Override + public String getSQL() { + return GET_PROJECT_STATE; + } + + @Override + public ProjectState processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + if (resultSet.getTimestamp("last_accessed") == null) { + return ProjectState.SWAPPED; + } + return ProjectState.PRESENT; } + return ProjectState.NOT_PRESENT; + } + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setString(1, projectName); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetSwapCompression.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetSwapCompression.java index c7f4d5b510..b21ec3e786 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetSwapCompression.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/GetSwapCompression.java @@ -1,14 +1,13 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; public class GetSwapCompression implements SQLQuery { private static final String GET_SWAP_COMPRESSION = - "SELECT `swap_compression` FROM `projects` WHERE `name` = ?"; + "SELECT `swap_compression` FROM `projects` WHERE `name` = ?"; private final String projectName; @@ -31,9 +30,7 @@ public class GetSwapCompression implements SQLQuery { } @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { + public void addParametersToStatement(PreparedStatement statement) throws SQLException { statement.setString(1, projectName); } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/LastAccessedColumnExists.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/LastAccessedColumnExists.java index da3b525ff3..758a47b21a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/LastAccessedColumnExists.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/LastAccessedColumnExists.java @@ -1,31 +1,28 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; -/** +/* * Created by winston on 04/09/2016. */ public class LastAccessedColumnExists implements SQLQuery { - private static final String LAST_ACCESSED_COLUMN_EXISTS = - "PRAGMA table_info(`projects`)"; + private static final String LAST_ACCESSED_COLUMN_EXISTS = "PRAGMA table_info(`projects`)"; - @Override - public String getSQL() { - return LAST_ACCESSED_COLUMN_EXISTS; + @Override + public String getSQL() { + return LAST_ACCESSED_COLUMN_EXISTS; + } + + @Override + public Boolean processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + if (resultSet.getString(2).equals("last_accessed")) { + return true; + } } - - @Override - public Boolean processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - if (resultSet.getString(2).equals("last_accessed")) { - return true; - } - } - return false; - } - + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/RestoreTimeColumnExists.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/RestoreTimeColumnExists.java index ed4d0567ae..c726bec2de 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/RestoreTimeColumnExists.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/RestoreTimeColumnExists.java @@ -1,26 +1,24 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; public class RestoreTimeColumnExists implements SQLQuery { - private static final String RESTORE_TIME_COLUMN_EXISTS = - "PRAGMA table_info(`projects`)"; + private static final String RESTORE_TIME_COLUMN_EXISTS = "PRAGMA table_info(`projects`)"; - @Override - public String getSQL() { - return RESTORE_TIME_COLUMN_EXISTS; - } + @Override + public String getSQL() { + return RESTORE_TIME_COLUMN_EXISTS; + } - @Override - public Boolean processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - if (resultSet.getString(2).equals("restore_time")) { - return true; - } - } - return false; + @Override + public Boolean processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + if (resultSet.getString(2).equals("restore_time")) { + return true; + } } + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapCompressionColumnExists.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapCompressionColumnExists.java index 1b1754f26e..89f083a5d5 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapCompressionColumnExists.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapCompressionColumnExists.java @@ -1,27 +1,24 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; public class SwapCompressionColumnExists implements SQLQuery { - private static final String SWAP_COMPRESSION_COLUMN_EXISTS = - "PRAGMA table_info(`projects`)"; + private static final String SWAP_COMPRESSION_COLUMN_EXISTS = "PRAGMA table_info(`projects`)"; - @Override - public String getSQL() { - return SWAP_COMPRESSION_COLUMN_EXISTS; + @Override + public String getSQL() { + return SWAP_COMPRESSION_COLUMN_EXISTS; + } + + @Override + public Boolean processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + if (resultSet.getString(2).equals("swap_compression")) { + return true; + } } - - @Override - public Boolean processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - if (resultSet.getString(2).equals("swap_compression")) { - return true; - } - } - return false; - } - + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapTimeColumnExists.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapTimeColumnExists.java index 9426dedc5f..7d65e21bb2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapTimeColumnExists.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/query/SwapTimeColumnExists.java @@ -1,27 +1,24 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.query; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; - import java.sql.ResultSet; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLQuery; public class SwapTimeColumnExists implements SQLQuery { - private static final String SWAP_TIME_COLUMN_EXISTS = - "PRAGMA table_info(`projects`)"; + private static final String SWAP_TIME_COLUMN_EXISTS = "PRAGMA table_info(`projects`)"; - @Override - public String getSQL() { - return SWAP_TIME_COLUMN_EXISTS; + @Override + public String getSQL() { + return SWAP_TIME_COLUMN_EXISTS; + } + + @Override + public Boolean processResultSet(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + if (resultSet.getString(2).equals("swap_time")) { + return true; + } } - - @Override - public Boolean processResultSet(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - if (resultSet.getString(2).equals("swap_time")) { - return true; - } - } - return false; - } - + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddLastAccessed.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddLastAccessed.java index 549d4403b2..31bbadbd0c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddLastAccessed.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddLastAccessed.java @@ -2,18 +2,16 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.alter; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by winston on 03/09/2016. */ public class ProjectsAddLastAccessed implements SQLUpdate { - private static final String PROJECTS_ADD_LAST_ACCESSED = - "ALTER TABLE `projects`\n" + - "ADD COLUMN `last_accessed` DATETIME NULL DEFAULT 0"; - - @Override - public String getSQL() { - return PROJECTS_ADD_LAST_ACCESSED; - } + private static final String PROJECTS_ADD_LAST_ACCESSED = + "ALTER TABLE `projects`\n" + "ADD COLUMN `last_accessed` DATETIME NULL DEFAULT 0"; + @Override + public String getSQL() { + return PROJECTS_ADD_LAST_ACCESSED; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddRestoreTime.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddRestoreTime.java index dd92efcab5..d4675db1d0 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddRestoreTime.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddRestoreTime.java @@ -4,8 +4,7 @@ import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class ProjectsAddRestoreTime implements SQLUpdate { private static final String PROJECTS_ADD_RESTORE_TIME = - "ALTER TABLE `projects`\n" + - "ADD COLUMN `restore_time` DATETIME NULL;\n"; + "ALTER TABLE `projects`\n" + "ADD COLUMN `restore_time` DATETIME NULL;\n"; @Override public String getSQL() { diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapCompression.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapCompression.java index c5599d9116..25ec93048b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapCompression.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapCompression.java @@ -4,8 +4,7 @@ import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class ProjectsAddSwapCompression implements SQLUpdate { private static final String PROJECTS_ADD_SWAP_COMPRESSION = - "ALTER TABLE `projects`\n" + - "ADD COLUMN `swap_compression` VARCHAR NULL;\n"; + "ALTER TABLE `projects`\n" + "ADD COLUMN `swap_compression` VARCHAR NULL;\n"; @Override public String getSQL() { diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapTime.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapTime.java index da89be8c25..e578dc0c9a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapTime.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/ProjectsAddSwapTime.java @@ -4,12 +4,10 @@ import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class ProjectsAddSwapTime implements SQLUpdate { private static final String PROJECTS_ADD_SWAP_TIME = - "ALTER TABLE `projects`\n" + - "ADD COLUMN `swap_time` DATETIME NULL;\n"; + "ALTER TABLE `projects`\n" + "ADD COLUMN `swap_time` DATETIME NULL;\n"; @Override public String getSQL() { return PROJECTS_ADD_SWAP_TIME; } - } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/SetSoftHeapLimitPragma.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/SetSoftHeapLimitPragma.java index da1a98cfa5..0f8d935274 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/SetSoftHeapLimitPragma.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/alter/SetSoftHeapLimitPragma.java @@ -3,15 +3,14 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.alter; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class SetSoftHeapLimitPragma implements SQLUpdate { - private int heapLimitBytes = 0; + private int heapLimitBytes = 0; - public SetSoftHeapLimitPragma(int heapLimitBytes) { - this.heapLimitBytes = heapLimitBytes; - } - - @Override - public String getSQL() { - return "PRAGMA soft_heap_limit="+this.heapLimitBytes+";"; - } + public SetSoftHeapLimitPragma(int heapLimitBytes) { + this.heapLimitBytes = heapLimitBytes; + } + @Override + public String getSQL() { + return "PRAGMA soft_heap_limit=" + this.heapLimitBytes + ";"; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateIndexURLIndexStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateIndexURLIndexStore.java index 9b89199b65..a40842227f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateIndexURLIndexStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateIndexURLIndexStore.java @@ -2,18 +2,17 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.create; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by Winston on 21/02/15. */ public class CreateIndexURLIndexStore implements SQLUpdate { - public static final String CREATE_INDEX_URL_INDEX_STORE = - "CREATE UNIQUE INDEX IF NOT EXISTS `project_path_index` " + - "ON `url_index_store`(`project_name`, `path`);\n"; - - @Override - public String getSQL() { - return CREATE_INDEX_URL_INDEX_STORE; - } + public static final String CREATE_INDEX_URL_INDEX_STORE = + "CREATE UNIQUE INDEX IF NOT EXISTS `project_path_index` " + + "ON `url_index_store`(`project_name`, `path`);\n"; + @Override + public String getSQL() { + return CREATE_INDEX_URL_INDEX_STORE; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsIndexLastAccessed.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsIndexLastAccessed.java index 6e129d8fee..f937fa3b63 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsIndexLastAccessed.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsIndexLastAccessed.java @@ -2,18 +2,17 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.create; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by winston on 23/08/2016. */ public class CreateProjectsIndexLastAccessed implements SQLUpdate { - private static final String CREATE_PROJECTS_INDEX_LAST_ACCESSED = - "CREATE INDEX IF NOT EXISTS `projects_index_last_accessed`\n" + - " ON `projects`(`last_accessed`)"; - - @Override - public String getSQL() { - return CREATE_PROJECTS_INDEX_LAST_ACCESSED; - } + private static final String CREATE_PROJECTS_INDEX_LAST_ACCESSED = + "CREATE INDEX IF NOT EXISTS `projects_index_last_accessed`\n" + + " ON `projects`(`last_accessed`)"; + @Override + public String getSQL() { + return CREATE_PROJECTS_INDEX_LAST_ACCESSED; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsTableSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsTableSQLUpdate.java index 15042445ad..1e7910c247 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsTableSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateProjectsTableSQLUpdate.java @@ -2,25 +2,24 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.create; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by Winston on 20/11/14. */ public class CreateProjectsTableSQLUpdate implements SQLUpdate { - private static final String CREATE_PROJECTS_TABLE = - "CREATE TABLE IF NOT EXISTS `projects` (\n" + - " `name` VARCHAR NOT NULL DEFAULT '',\n" + - " `version_id` INT NOT NULL DEFAULT 0,\n" + - " `last_accessed` DATETIME NULL DEFAULT 0,\n" + - " `swap_time` DATETIME NULL,\n" + - " `restore_time` DATETIME NULL,\n" + - " `swap_compression` VARCHAR NULL,\n" + - " PRIMARY KEY (`name`)\n" + - ")"; - - @Override - public String getSQL() { - return CREATE_PROJECTS_TABLE; - } + private static final String CREATE_PROJECTS_TABLE = + "CREATE TABLE IF NOT EXISTS `projects` (\n" + + " `name` VARCHAR NOT NULL DEFAULT '',\n" + + " `version_id` INT NOT NULL DEFAULT 0,\n" + + " `last_accessed` DATETIME NULL DEFAULT 0,\n" + + " `swap_time` DATETIME NULL,\n" + + " `restore_time` DATETIME NULL,\n" + + " `swap_compression` VARCHAR NULL,\n" + + " PRIMARY KEY (`name`)\n" + + ")"; + @Override + public String getSQL() { + return CREATE_PROJECTS_TABLE; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateURLIndexStoreSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateURLIndexStoreSQLUpdate.java index 3ca0e9cf4c..1274eb0a59 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateURLIndexStoreSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/create/CreateURLIndexStoreSQLUpdate.java @@ -2,27 +2,26 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.create; import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by Winston on 20/11/14. */ public class CreateURLIndexStoreSQLUpdate implements SQLUpdate { - private static final String CREATE_URL_INDEX_STORE = - "CREATE TABLE IF NOT EXISTS `url_index_store` (\n"+ - " `project_name` varchar(10) NOT NULL DEFAULT '',\n"+ - " `url` text NOT NULL,\n"+ - " `path` text NOT NULL,\n"+ - " PRIMARY KEY (`project_name`,`url`),\n"+ - " CONSTRAINT `url_index_store_ibfk_1` " + - "FOREIGN KEY (`project_name`) " + - "REFERENCES `projects` (`name`) " + - "ON DELETE CASCADE " + - "ON UPDATE CASCADE\n"+ - ");\n"; - - @Override - public String getSQL() { - return CREATE_URL_INDEX_STORE; - } + private static final String CREATE_URL_INDEX_STORE = + "CREATE TABLE IF NOT EXISTS `url_index_store` (\n" + + " `project_name` varchar(10) NOT NULL DEFAULT '',\n" + + " `url` text NOT NULL,\n" + + " `path` text NOT NULL,\n" + + " PRIMARY KEY (`project_name`,`url`),\n" + + " CONSTRAINT `url_index_store_ibfk_1` " + + "FOREIGN KEY (`project_name`) " + + "REFERENCES `projects` (`name`) " + + "ON DELETE CASCADE " + + "ON UPDATE CASCADE\n" + + ");\n"; + @Override + public String getSQL() { + return CREATE_URL_INDEX_STORE; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteAllFilesInProjectSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteAllFilesInProjectSQLUpdate.java index dd6e15daed..a23a468fcc 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteAllFilesInProjectSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteAllFilesInProjectSQLUpdate.java @@ -2,7 +2,6 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.delete; import java.sql.PreparedStatement; import java.sql.SQLException; - import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class DeleteAllFilesInProjectSQLUpdate implements SQLUpdate { diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdate.java index 4390ebe9a0..5c3527a2ec 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdate.java @@ -1,53 +1,43 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.delete; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; - import java.sql.PreparedStatement; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by Winston on 20/11/14. */ public class DeleteFilesForProjectSQLUpdate implements SQLUpdate { - private static final String DELETE_URL_INDEXES_FOR_PROJECT_NAME = - "DELETE FROM `url_index_store` " + - "WHERE `project_name` = ? AND path IN ("; + private static final String DELETE_URL_INDEXES_FOR_PROJECT_NAME = + "DELETE FROM `url_index_store` " + "WHERE `project_name` = ? AND path IN ("; - private final String projectName; - private final String[] paths; + private final String projectName; + private final String[] paths; - public DeleteFilesForProjectSQLUpdate( - String projectName, - String... paths - ) { - this.projectName = projectName; - this.paths = paths; + public DeleteFilesForProjectSQLUpdate(String projectName, String... paths) { + this.projectName = projectName; + this.paths = paths; + } + + @Override + public String getSQL() { + StringBuilder sb = new StringBuilder(DELETE_URL_INDEXES_FOR_PROJECT_NAME); + for (int i = 0; i < paths.length; i++) { + sb.append("?"); + if (i < paths.length - 1) { + sb.append(", "); + } } + sb.append(");\n"); + return sb.toString(); + } - @Override - public String getSQL() { - StringBuilder sb = new StringBuilder( - DELETE_URL_INDEXES_FOR_PROJECT_NAME - ); - for (int i = 0; i < paths.length; i++) { - sb.append("?"); - if (i < paths.length - 1) { - sb.append(", "); - } - } - sb.append(");\n"); - return sb.toString(); + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setString(1, projectName); + for (int i = 0; i < paths.length; i++) { + statement.setString(i + 2, paths[i]); } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setString(1, projectName); - for (int i = 0; i < paths.length; i++) { - statement.setString(i + 2, paths[i]); - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteProjectSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteProjectSQLUpdate.java index 64153cf504..ff989e16c1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteProjectSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteProjectSQLUpdate.java @@ -2,7 +2,6 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.delete; import java.sql.PreparedStatement; import java.sql.SQLException; - import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class DeleteProjectSQLUpdate implements SQLUpdate { diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/AddURLIndexSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/AddURLIndexSQLUpdate.java index 5c337bed7a..a79ea6d078 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/AddURLIndexSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/AddURLIndexSQLUpdate.java @@ -1,45 +1,41 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.insert; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; - import java.sql.PreparedStatement; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by Winston on 20/11/14. */ public class AddURLIndexSQLUpdate implements SQLUpdate { - private static final String ADD_URL_INDEX = - "INSERT OR REPLACE INTO `url_index_store`(" + - "`project_name`, " + - "`url`, " + - "`path`" + - ") VALUES " + - "(?, ?, ?)\n"; + private static final String ADD_URL_INDEX = + "INSERT OR REPLACE INTO `url_index_store`(" + + "`project_name`, " + + "`url`, " + + "`path`" + + ") VALUES " + + "(?, ?, ?)\n"; - private final String projectName; - private final String url; - private final String path; + private final String projectName; + private final String url; + private final String path; - public AddURLIndexSQLUpdate(String projectName, String url, String path) { - this.projectName = projectName; - this.url = url; - this.path = path; - } + public AddURLIndexSQLUpdate(String projectName, String url, String path) { + this.projectName = projectName; + this.url = url; + this.path = path; + } - @Override - public String getSQL() { - return ADD_URL_INDEX; - } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setString(1, projectName); - statement.setString(2, url); - statement.setString(3, path); - } + @Override + public String getSQL() { + return ADD_URL_INDEX; + } + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setString(1, projectName); + statement.setString(2, url); + statement.setString(3, path); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectLastAccessedTime.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectLastAccessedTime.java index 7870822a50..945f3988d1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectLastAccessedTime.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectLastAccessedTime.java @@ -1,43 +1,34 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.insert; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; - import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by winston on 23/08/2016. */ public class SetProjectLastAccessedTime implements SQLUpdate { - private static final String SET_PROJECT_LAST_ACCESSED_TIME = - "UPDATE `projects`\n" + - "SET `last_accessed` = ?\n" + - "WHERE `name` = ?"; + private static final String SET_PROJECT_LAST_ACCESSED_TIME = + "UPDATE `projects`\n" + "SET `last_accessed` = ?\n" + "WHERE `name` = ?"; - private final String projectName; - private final Timestamp lastAccessed; + private final String projectName; + private final Timestamp lastAccessed; - public SetProjectLastAccessedTime( - String projectName, - Timestamp lastAccessed - ) { - this.projectName = projectName; - this.lastAccessed = lastAccessed; - } + public SetProjectLastAccessedTime(String projectName, Timestamp lastAccessed) { + this.projectName = projectName; + this.lastAccessed = lastAccessed; + } - @Override - public String getSQL() { - return SET_PROJECT_LAST_ACCESSED_TIME; - } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setTimestamp(1, lastAccessed); - statement.setString(2, projectName); - } + @Override + public String getSQL() { + return SET_PROJECT_LAST_ACCESSED_TIME; + } + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setTimestamp(1, lastAccessed); + statement.setString(2, projectName); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectSQLUpdate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectSQLUpdate.java index 717beaec05..7c472e0fb7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectSQLUpdate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/SetProjectSQLUpdate.java @@ -1,39 +1,35 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.insert; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; - import java.sql.PreparedStatement; import java.sql.SQLException; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; -/** +/* * Created by Winston on 20/11/14. */ public class SetProjectSQLUpdate implements SQLUpdate { - private static final String SET_PROJECT = - "INSERT OR REPLACE " - + "INTO `projects`(`name`, `version_id`, `last_accessed`) " - + "VALUES (?, ?, DATETIME('now'));\n"; + private static final String SET_PROJECT = + "INSERT OR REPLACE " + + "INTO `projects`(`name`, `version_id`, `last_accessed`) " + + "VALUES (?, ?, DATETIME('now'));\n"; - private final String projectName; - private final int versionID; + private final String projectName; + private final int versionID; - public SetProjectSQLUpdate(String projectName, int versionID) { - this.projectName = projectName; - this.versionID = versionID; - } + public SetProjectSQLUpdate(String projectName, int versionID) { + this.projectName = projectName; + this.versionID = versionID; + } - @Override - public String getSQL() { - return SET_PROJECT; - } - - @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { - statement.setString(1, projectName); - statement.setInt(2, versionID); - } + @Override + public String getSQL() { + return SET_PROJECT; + } + @Override + public void addParametersToStatement(PreparedStatement statement) throws SQLException { + statement.setString(1, projectName); + statement.setInt(2, versionID); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateRestore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateRestore.java index 91abc5f473..bb76748b6c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateRestore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateRestore.java @@ -1,20 +1,19 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.insert; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; - import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; import java.time.LocalDateTime; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class UpdateRestore implements SQLUpdate { private static final String UPDATE_RESTORE = - "UPDATE `projects`\n" + - "SET `last_accessed` = ?,\n" + - " `swap_time` = NULL,\n" + - " `restore_time` = ?,\n" + - " `swap_compression` = NULL\n" + - "WHERE `name` = ?;\n"; + "UPDATE `projects`\n" + + "SET `last_accessed` = ?,\n" + + " `swap_time` = NULL,\n" + + " `restore_time` = ?,\n" + + " `swap_compression` = NULL\n" + + "WHERE `name` = ?;\n"; private final String projectName; private final Timestamp now; @@ -30,9 +29,7 @@ public class UpdateRestore implements SQLUpdate { } @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { + public void addParametersToStatement(PreparedStatement statement) throws SQLException { statement.setTimestamp(1, now); statement.setTimestamp(2, now); statement.setString(3, projectName); diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateSwap.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateSwap.java index 24d7815279..df09ddcdf6 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateSwap.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/insert/UpdateSwap.java @@ -1,20 +1,19 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.insert; -import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; - import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; import java.time.LocalDateTime; +import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SQLUpdate; public class UpdateSwap implements SQLUpdate { private static final String UPDATE_SWAP = - "UPDATE `projects`\n" + - "SET `last_accessed` = NULL,\n" + - " `swap_time` = ?,\n" + - " `restore_time` = NULL,\n" + - " `swap_compression` = ?\n" + - "WHERE `name` = ?;\n"; + "UPDATE `projects`\n" + + "SET `last_accessed` = NULL,\n" + + " `swap_time` = ?,\n" + + " `restore_time` = NULL,\n" + + " `swap_compression` = ?\n" + + "WHERE `name` = ?;\n"; private final String projectName; private final String compression; @@ -32,9 +31,7 @@ public class UpdateSwap implements SQLUpdate { } @Override - public void addParametersToStatement( - PreparedStatement statement - ) throws SQLException { + public void addParametersToStatement(PreparedStatement statement) throws SQLException { statement.setTimestamp(1, now); statement.setString(2, compression); statement.setString(3, projectName); diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJob.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJob.java index 74f965d7a4..60edc7a268 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJob.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJob.java @@ -1,13 +1,8 @@ package uk.ac.ic.wlgitbridge.bridge.gc; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.bridge.repo.ProjectRepo; -import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; - -import java.util.Optional; import java.util.concurrent.CompletableFuture; -/** +/* * Is started by the bridge. Every time a project is updated, we queue it for * GC which executes every hour or so. * @@ -20,15 +15,15 @@ import java.util.concurrent.CompletableFuture; */ public interface GcJob { - void start(); + void start(); - void stop(); + void stop(); - void onPreGc(Runnable preGc); + void onPreGc(Runnable preGc); - void onPostGc(Runnable postGc); + void onPostGc(Runnable postGc); - void queueForGc(String projectName); + void queueForGc(String projectName); - CompletableFuture waitForRun(); + CompletableFuture waitForRun(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImpl.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImpl.java index 37b49432c1..6ccf1dd8a1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImpl.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImpl.java @@ -1,13 +1,5 @@ package uk.ac.ic.wlgitbridge.bridge.gc; -import uk.ac.ic.wlgitbridge.bridge.lock.LockGuard; -import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; -import uk.ac.ic.wlgitbridge.bridge.repo.ProjectRepo; -import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; -import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; -import uk.ac.ic.wlgitbridge.util.Log; -import uk.ac.ic.wlgitbridge.util.TimerUtils; - import java.io.IOException; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -16,129 +8,123 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import uk.ac.ic.wlgitbridge.bridge.lock.LockGuard; +import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; +import uk.ac.ic.wlgitbridge.bridge.repo.ProjectRepo; +import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; +import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; +import uk.ac.ic.wlgitbridge.util.Log; +import uk.ac.ic.wlgitbridge.util.TimerUtils; -/** +/* * Implementation of {@link GcJob} using its own Timer and a synchronized * queue. */ public class GcJobImpl implements GcJob { - private final RepoStore repoStore; - private final ProjectLock locks; + private final RepoStore repoStore; + private final ProjectLock locks; - private final long intervalMs; - private final Timer timer; + private final long intervalMs; + private final Timer timer; - private final Set gcQueue; + private final Set gcQueue; - /** - * Hooks in case they are needed, e.g. for testing. - */ - private AtomicReference preGc; - private AtomicReference postGc; + /* + * Hooks in case they are needed, e.g. for testing. + */ + private AtomicReference preGc; + private AtomicReference postGc; - /* We need to iterate over and empty it after every run */ - private final Lock jobWaitersLock; - private final List> jobWaiters; + /* We need to iterate over and empty it after every run */ + private final Lock jobWaitersLock; + private final List> jobWaiters; - public GcJobImpl(RepoStore repoStore, ProjectLock locks, long intervalMs) { - this.repoStore = repoStore; - this.locks = locks; - this.intervalMs = intervalMs; - timer = new Timer(); - gcQueue = Collections.newSetFromMap(new ConcurrentHashMap<>()); - preGc = new AtomicReference<>(() -> {}); - postGc = new AtomicReference<>(() -> {}); - jobWaitersLock = new ReentrantLock(); - jobWaiters = new ArrayList<>(); + public GcJobImpl(RepoStore repoStore, ProjectLock locks, long intervalMs) { + this.repoStore = repoStore; + this.locks = locks; + this.intervalMs = intervalMs; + timer = new Timer(); + gcQueue = Collections.newSetFromMap(new ConcurrentHashMap<>()); + preGc = new AtomicReference<>(() -> {}); + postGc = new AtomicReference<>(() -> {}); + jobWaitersLock = new ReentrantLock(); + jobWaiters = new ArrayList<>(); + } + + public GcJobImpl(RepoStore repoStore, ProjectLock locks) { + this(repoStore, locks, TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)); + } + + @Override + public void start() { + Log.info("Starting GC job to run every [{}] ms", intervalMs); + timer.scheduleAtFixedRate(TimerUtils.makeTimerTask(this::doGC), intervalMs, intervalMs); + } + + @Override + public void stop() { + Log.info("Stopping GC job"); + timer.cancel(); + } + + @Override + public void onPreGc(Runnable preGc) { + this.preGc.set(preGc); + } + + @Override + public void onPostGc(Runnable postGc) { + this.postGc.set(postGc); + } + + /* + * Needs to be callable from any thread. + * @param projectName + */ + @Override + public void queueForGc(String projectName) { + gcQueue.add(projectName); + } + + @Override + public CompletableFuture waitForRun() { + CompletableFuture ret = new CompletableFuture<>(); + jobWaitersLock.lock(); + try { + jobWaiters.add(ret); + } finally { + jobWaitersLock.unlock(); } + return ret; + } - public GcJobImpl(RepoStore repoStore, ProjectLock locks) { - this( - repoStore, - locks, - TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS) - ); - } - - @Override - public void start() { - Log.info("Starting GC job to run every [{}] ms", intervalMs); - timer.scheduleAtFixedRate( - TimerUtils.makeTimerTask(this::doGC), - intervalMs, - intervalMs - ); - } - - @Override - public void stop() { - Log.info("Stopping GC job"); - timer.cancel(); - } - - @Override - public void onPreGc(Runnable preGc) { - this.preGc.set(preGc); - } - - @Override - public void onPostGc(Runnable postGc) { - this.postGc.set(postGc); - } - - /** - * Needs to be callable from any thread. - * @param projectName - */ - @Override - public void queueForGc(String projectName) { - gcQueue.add(projectName); - } - - @Override - public CompletableFuture waitForRun() { - CompletableFuture ret = new CompletableFuture<>(); - jobWaitersLock.lock(); + private void doGC() { + Log.info("GC job running"); + int numGcs = 0; + preGc.get().run(); + for (Iterator it = gcQueue.iterator(); it.hasNext(); it.remove(), ++numGcs) { + String proj = it.next(); + Log.debug("[{}] Running GC job on project", proj); + try (LockGuard __ = locks.lockGuard(proj)) { try { - jobWaiters.add(ret); - } finally { - jobWaitersLock.unlock(); + ProjectRepo repo = repoStore.getExistingRepo(proj); + repo.runGC(); + repo.deleteIncomingPacks(); + } catch (IOException e) { + Log.warn("[{}] Failed to GC project", proj); } - return ret; + } catch (CannotAcquireLockException e) { + Log.warn("[{}] Cannot acquire project lock, skipping GC", proj); + } } - - private void doGC() { - Log.info("GC job running"); - int numGcs = 0; - preGc.get().run(); - for ( - Iterator it = gcQueue.iterator(); - it.hasNext(); - it.remove(), ++numGcs - ) { - String proj = it.next(); - Log.debug("[{}] Running GC job on project", proj); - try (LockGuard __ = locks.lockGuard(proj)) { - try { - ProjectRepo repo = repoStore.getExistingRepo(proj); - repo.runGC(); - repo.deleteIncomingPacks(); - } catch (IOException e) { - Log.warn("[{}] Failed to GC project", proj); - } - } catch (CannotAcquireLockException e) { - Log.warn("[{}] Cannot acquire project lock, skipping GC", proj); - } - } - Log.info("GC job finished, num gcs: {}", numGcs); - jobWaitersLock.lock(); - try { - jobWaiters.forEach(w -> w.complete(null)); - } finally { - jobWaitersLock.unlock(); - } - postGc.get().run(); + Log.info("GC job finished, num gcs: {}", numGcs); + jobWaitersLock.lock(); + try { + jobWaiters.forEach(w -> w.complete(null)); + } finally { + jobWaitersLock.unlock(); } - + postGc.get().run(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/LockGuard.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/LockGuard.java index 73c8d253c1..9fc761a7c2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/LockGuard.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/LockGuard.java @@ -1,10 +1,9 @@ package uk.ac.ic.wlgitbridge.bridge.lock; -/** +/* * Created by winston on 24/08/2016. */ public interface LockGuard extends AutoCloseable { - void close(); - + void close(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/ProjectLock.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/ProjectLock.java index f4617d258c..e23c23c2e6 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/ProjectLock.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/lock/ProjectLock.java @@ -2,7 +2,7 @@ package uk.ac.ic.wlgitbridge.bridge.lock; import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; -/** +/* * Project Lock class. * * The locks should be re-entrant. For example, we are usually holding the lock @@ -10,16 +10,15 @@ import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; */ public interface ProjectLock { - void lockAll(); + void lockAll(); - void lockForProject(String projectName) throws CannotAcquireLockException; + void lockForProject(String projectName) throws CannotAcquireLockException; - void unlockForProject(String projectName); - - /* RAII hahaha */ - default LockGuard lockGuard(String projectName) throws CannotAcquireLockException { - lockForProject(projectName); - return () -> unlockForProject(projectName); - } + void unlockForProject(String projectName); + /* RAII hahaha */ + default LockGuard lockGuard(String projectName) throws CannotAcquireLockException { + lockForProject(projectName); + return () -> unlockForProject(projectName); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStore.java index e2fadc05a0..d774f30652 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStore.java @@ -1,13 +1,8 @@ package uk.ac.ic.wlgitbridge.bridge.repo; -import com.google.api.client.repackaged.com.google.common.base.Preconditions; -import org.apache.commons.io.FileUtils; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Repository; -import uk.ac.ic.wlgitbridge.util.Log; -import uk.ac.ic.wlgitbridge.util.Project; -import uk.ac.ic.wlgitbridge.util.Tar; +import static uk.ac.ic.wlgitbridge.util.Util.deleteInDirectoryApartFrom; +import com.google.api.client.repackaged.com.google.common.base.Preconditions; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -17,201 +12,153 @@ import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Function; +import org.apache.commons.io.FileUtils; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import uk.ac.ic.wlgitbridge.util.Log; +import uk.ac.ic.wlgitbridge.util.Project; +import uk.ac.ic.wlgitbridge.util.Tar; -import static uk.ac.ic.wlgitbridge.util.Util.deleteInDirectoryApartFrom; - -/** +/* * Created by winston on 20/08/2016. */ public class FSGitRepoStore implements RepoStore { - private static final long DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024; + private static final long DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024; - private final String repoStorePath; + private final String repoStorePath; - private final File rootDirectory; + private final File rootDirectory; - private final long maxFileSize; + private final long maxFileSize; - private final Function fsSizer; + private final Function fsSizer; - public FSGitRepoStore( - String repoStorePath, - Optional maxFileSize - ) { - this( - repoStorePath, - maxFileSize.orElse(DEFAULT_MAX_FILE_SIZE), - d -> d.getTotalSpace() - d.getFreeSpace() - ); - } + public FSGitRepoStore(String repoStorePath, Optional maxFileSize) { + this( + repoStorePath, + maxFileSize.orElse(DEFAULT_MAX_FILE_SIZE), + d -> d.getTotalSpace() - d.getFreeSpace()); + } - public FSGitRepoStore( - String repoStorePath, - long maxFileSize, - Function fsSizer - ) { - this.repoStorePath = repoStorePath; - rootDirectory = initRootGitDirectory(repoStorePath); - this.maxFileSize = maxFileSize; - this.fsSizer = fsSizer; - } + public FSGitRepoStore(String repoStorePath, long maxFileSize, Function fsSizer) { + this.repoStorePath = repoStorePath; + rootDirectory = initRootGitDirectory(repoStorePath); + this.maxFileSize = maxFileSize; + this.fsSizer = fsSizer; + } - @Override - public String getRepoStorePath() { - return repoStorePath; - } + @Override + public String getRepoStorePath() { + return repoStorePath; + } - @Override - public File getRootDirectory() { - return rootDirectory; - } + @Override + public File getRootDirectory() { + return rootDirectory; + } - @Override - public ProjectRepo initRepo(String project) throws IOException { - GitProjectRepo ret = GitProjectRepo.fromName(project); - ret.initRepo(this); - return new WalkOverrideGitRepo( - ret, Optional.of(maxFileSize), Optional.empty()); - } + @Override + public ProjectRepo initRepo(String project) throws IOException { + GitProjectRepo ret = GitProjectRepo.fromName(project); + ret.initRepo(this); + return new WalkOverrideGitRepo(ret, Optional.of(maxFileSize), Optional.empty()); + } - @Override - public ProjectRepo getExistingRepo(String project) throws IOException { - GitProjectRepo ret = GitProjectRepo.fromName(project); - ret.useExistingRepository(this); - return new WalkOverrideGitRepo( - ret, Optional.of(maxFileSize), Optional.empty()); - } + @Override + public ProjectRepo getExistingRepo(String project) throws IOException { + GitProjectRepo ret = GitProjectRepo.fromName(project); + ret.useExistingRepository(this); + return new WalkOverrideGitRepo(ret, Optional.of(maxFileSize), Optional.empty()); + } - @Override - public ProjectRepo useJGitRepo(Repository repo, ObjectId commitId) { - GitProjectRepo ret = GitProjectRepo.fromJGitRepo(repo); - return new WalkOverrideGitRepo( - ret, Optional.of(maxFileSize), Optional.of(commitId)); - } + @Override + public ProjectRepo useJGitRepo(Repository repo, ObjectId commitId) { + GitProjectRepo ret = GitProjectRepo.fromJGitRepo(repo); + return new WalkOverrideGitRepo(ret, Optional.of(maxFileSize), Optional.of(commitId)); + } - /* TODO: Perhaps we should just delete bad directories on the fly. */ - @Override - public void purgeNonexistentProjects( - Collection existingProjectNames - ) { - List excludedFromDeletion = - new ArrayList<>(existingProjectNames); - excludedFromDeletion.add(".wlgb"); - deleteInDirectoryApartFrom( - rootDirectory, - excludedFromDeletion.toArray(new String[] {}) - ); - } + /* TODO: Perhaps we should just delete bad directories on the fly. */ + @Override + public void purgeNonexistentProjects(Collection existingProjectNames) { + List excludedFromDeletion = new ArrayList<>(existingProjectNames); + excludedFromDeletion.add(".wlgb"); + deleteInDirectoryApartFrom(rootDirectory, excludedFromDeletion.toArray(new String[] {})); + } - @Override - public long totalSize() { - return fsSizer.apply(rootDirectory); - } + @Override + public long totalSize() { + return fsSizer.apply(rootDirectory); + } - @Override - public InputStream bzip2Project( - String projectName, - long[] sizePtr - ) throws IOException { - Project.checkValidProjectName(projectName); - Log.debug("[{}] bzip2 project", projectName); - return Tar.bz2.zip(getDotGitForProject(projectName), sizePtr); - } + @Override + public InputStream bzip2Project(String projectName, long[] sizePtr) throws IOException { + Project.checkValidProjectName(projectName); + Log.debug("[{}] bzip2 project", projectName); + return Tar.bz2.zip(getDotGitForProject(projectName), sizePtr); + } - @Override - public InputStream gzipProject( - String projectName, - long[] sizePtr - ) throws IOException { - Project.checkValidProjectName(projectName); - Log.debug("[{}] gzip project", projectName); - return Tar.gzip.zip(getDotGitForProject(projectName), sizePtr); - } + @Override + public InputStream gzipProject(String projectName, long[] sizePtr) throws IOException { + Project.checkValidProjectName(projectName); + Log.debug("[{}] gzip project", projectName); + return Tar.gzip.zip(getDotGitForProject(projectName), sizePtr); + } - @Override - public void gcProject(String projectName) throws IOException { - Project.checkValidProjectName(projectName); - ProjectRepo repo = getExistingRepo(projectName); - repo.runGC(); - } + @Override + public void gcProject(String projectName) throws IOException { + Project.checkValidProjectName(projectName); + ProjectRepo repo = getExistingRepo(projectName); + repo.runGC(); + } - @Override - public void remove(String projectName) throws IOException { - Project.checkValidProjectName(projectName); - FileUtils.deleteDirectory(new File(rootDirectory, projectName)); - } + @Override + public void remove(String projectName) throws IOException { + Project.checkValidProjectName(projectName); + FileUtils.deleteDirectory(new File(rootDirectory, projectName)); + } - @Override - public void unbzip2Project( - String projectName, - InputStream dataStream - ) throws IOException { - Preconditions.checkArgument( - Project.isValidProjectName(projectName), - "[%s] invalid project name: ", - projectName - ); - Preconditions.checkState( - getDirForProject(projectName).mkdirs(), - "[%s] directories for " + - "evicted project already exist", - projectName - ); - Log.debug("[{}] un-bzip2 project", projectName); - Tar.bz2.unzip(dataStream, getDirForProject(projectName)); - } + @Override + public void unbzip2Project(String projectName, InputStream dataStream) throws IOException { + Preconditions.checkArgument( + Project.isValidProjectName(projectName), "[%s] invalid project name: ", projectName); + Preconditions.checkState( + getDirForProject(projectName).mkdirs(), + "[%s] directories for " + "evicted project already exist", + projectName); + Log.debug("[{}] un-bzip2 project", projectName); + Tar.bz2.unzip(dataStream, getDirForProject(projectName)); + } - @Override - public void ungzipProject( - String projectName, - InputStream dataStream - ) throws IOException { - Preconditions.checkArgument( - Project.isValidProjectName(projectName), - "[%s] invalid project name: ", - projectName - ); - Preconditions.checkState( - getDirForProject(projectName).mkdirs(), - "[%s] directories for " + - "evicted project already exist", - projectName - ); - Log.debug("[{}] un-gzip project", projectName); - Tar.gzip.unzip(dataStream, getDirForProject(projectName)); - } + @Override + public void ungzipProject(String projectName, InputStream dataStream) throws IOException { + Preconditions.checkArgument( + Project.isValidProjectName(projectName), "[%s] invalid project name: ", projectName); + Preconditions.checkState( + getDirForProject(projectName).mkdirs(), + "[%s] directories for " + "evicted project already exist", + projectName); + Log.debug("[{}] un-gzip project", projectName); + Tar.gzip.unzip(dataStream, getDirForProject(projectName)); + } - private File getDirForProject(String projectName) { - Project.checkValidProjectName(projectName); - return Paths.get( - rootDirectory.getAbsolutePath() - ).resolve( - projectName - ).toFile(); - } + private File getDirForProject(String projectName) { + Project.checkValidProjectName(projectName); + return Paths.get(rootDirectory.getAbsolutePath()).resolve(projectName).toFile(); + } - private File getDotGitForProject(String projectName) { - Project.checkValidProjectName(projectName); - return Paths.get( - rootDirectory.getAbsolutePath() - ).resolve( - projectName - ).resolve( - ".git" - ).toFile(); - } - - private File initRootGitDirectory(String rootGitDirectoryPath) { - File rootGitDirectory = new File(rootGitDirectoryPath); - rootGitDirectory.mkdirs(); - Preconditions.checkArgument( - rootGitDirectory.isDirectory(), - "given root git directory " + - "is not a directory: %s", - rootGitDirectory.getAbsolutePath() - ); - return rootGitDirectory; - } + private File getDotGitForProject(String projectName) { + Project.checkValidProjectName(projectName); + return Paths.get(rootDirectory.getAbsolutePath()).resolve(projectName).resolve(".git").toFile(); + } + private File initRootGitDirectory(String rootGitDirectoryPath) { + File rootGitDirectory = new File(rootGitDirectoryPath); + rootGitDirectory.mkdirs(); + Preconditions.checkArgument( + rootGitDirectory.isDirectory(), + "given root git directory " + "is not a directory: %s", + rootGitDirectory.getAbsolutePath()); + return rootGitDirectory; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java index 4f37b9eab6..86754f8ab4 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepo.java @@ -1,6 +1,15 @@ package uk.ac.ic.wlgitbridge.bridge.repo; import com.google.common.base.Preconditions; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; import org.apache.commons.io.IOUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand; @@ -16,17 +25,7 @@ import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.Project; import uk.ac.ic.wlgitbridge.util.Util; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.*; - -/** +/* * Class representing a Git repository. * * It stores the projectName and repo separately because the hooks need to be @@ -40,241 +39,193 @@ import java.util.*; */ public class GitProjectRepo implements ProjectRepo { - private final String projectName; - private Optional repository; + private final String projectName; + private Optional repository; - public static GitProjectRepo fromJGitRepo(Repository repo) { - return new GitProjectRepo( - repo.getWorkTree().getName(), Optional.of(repo)); + public static GitProjectRepo fromJGitRepo(Repository repo) { + return new GitProjectRepo(repo.getWorkTree().getName(), Optional.of(repo)); + } + + public static GitProjectRepo fromName(String projectName) { + return new GitProjectRepo(projectName, Optional.empty()); + } + + GitProjectRepo(String projectName, Optional repository) { + Preconditions.checkArgument(Project.isValidProjectName(projectName)); + this.projectName = projectName; + this.repository = repository; + } + + @Override + public String getProjectName() { + return projectName; + } + + @Override + public void initRepo(RepoStore repoStore) throws IOException { + initRepositoryField(repoStore); + Preconditions.checkState(repository.isPresent()); + Repository repo = this.repository.get(); + // TODO: assert that this is a fresh repo. At the moment, we can't be + // sure whether the repo to be init'd doesn't exist or is just fresh + // and we crashed / aborted while committing + if (repo.getObjectDatabase().exists()) return; + repo.create(); + } + + @Override + public void useExistingRepository(RepoStore repoStore) throws IOException { + initRepositoryField(repoStore); + Preconditions.checkState(repository.isPresent()); + Preconditions.checkState(repository.get().getObjectDatabase().exists()); + } + + @Override + public RawDirectory getDirectory() throws IOException, GitUserException { + Preconditions.checkState(repository.isPresent()); + return new RepositoryObjectTreeWalker(repository.get()).getDirectoryContents(Optional.empty()); + } + + @Override + public Collection commitAndGetMissing(GitDirectoryContents contents) throws IOException { + try { + return doCommitAndGetMissing(contents); + } catch (GitAPIException e) { + throw new IOException(e); } + } - public static GitProjectRepo fromName(String projectName) { - return new GitProjectRepo(projectName, Optional.empty()); + @Override + public void runGC() throws IOException { + Preconditions.checkState(repository.isPresent(), "Repo is not present"); + File dir = getProjectDir(); + Preconditions.checkState(dir.isDirectory()); + Log.debug("[{}] Running git gc", projectName); + Process proc = new ProcessBuilder("git", "gc").directory(dir).start(); + int exitCode; + try { + exitCode = proc.waitFor(); + Log.debug("Exit: {}", exitCode); + } catch (InterruptedException e) { + throw new RuntimeException(e); } - - GitProjectRepo(String projectName, Optional repository) { - Preconditions.checkArgument(Project.isValidProjectName(projectName)); - this.projectName = projectName; - this.repository = repository; + if (exitCode != 0) { + Log.warn("[{}] Git gc failed", dir.getAbsolutePath()); + Log.warn(IOUtils.toString(proc.getInputStream(), StandardCharsets.UTF_8)); + Log.warn(IOUtils.toString(proc.getErrorStream(), StandardCharsets.UTF_8)); + throw new IOException("git gc error"); } + Log.debug("[{}] git gc successful", projectName); + } - @Override - public String getProjectName() { - return projectName; - } + @Override + public void deleteIncomingPacks() throws IOException { + Log.debug("[{}] Checking for garbage `incoming` files", projectName); + Files.walkFileTree( + getDotGitDir().toPath(), + new FileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + return FileVisitResult.CONTINUE; + } - @Override - public void initRepo( - RepoStore repoStore - ) throws IOException { - initRepositoryField(repoStore); - Preconditions.checkState(repository.isPresent()); - Repository repo = this.repository.get(); - // TODO: assert that this is a fresh repo. At the moment, we can't be - // sure whether the repo to be init'd doesn't exist or is just fresh - // and we crashed / aborted while committing - if (repo.getObjectDatabase().exists()) return; - repo.create(); - } - - @Override - public void useExistingRepository( - RepoStore repoStore - ) throws IOException { - initRepositoryField(repoStore); - Preconditions.checkState(repository.isPresent()); - Preconditions.checkState( - repository.get().getObjectDatabase().exists() - ); - } - - @Override - public RawDirectory getDirectory() - throws IOException, GitUserException { - Preconditions.checkState(repository.isPresent()); - return new RepositoryObjectTreeWalker( - repository.get() - ).getDirectoryContents(Optional.empty()); - } - - @Override - public Collection commitAndGetMissing( - GitDirectoryContents contents - ) throws IOException { - try { - return doCommitAndGetMissing(contents); - } catch (GitAPIException e) { - throw new IOException(e); - } - } - - @Override - public void runGC() throws IOException { - Preconditions.checkState( - repository.isPresent(), - "Repo is not present" - ); - File dir = getProjectDir(); - Preconditions.checkState(dir.isDirectory()); - Log.debug("[{}] Running git gc", projectName); - Process proc = new ProcessBuilder( - "git", "gc" - ).directory(dir).start(); - int exitCode; - try { - exitCode = proc.waitFor(); - Log.debug("Exit: {}", exitCode); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - if (exitCode != 0) { - Log.warn("[{}] Git gc failed", dir.getAbsolutePath()); - Log.warn(IOUtils.toString( - proc.getInputStream(), - StandardCharsets.UTF_8 - )); - Log.warn(IOUtils.toString( - proc.getErrorStream(), - StandardCharsets.UTF_8 - )); - throw new IOException("git gc error"); - } - Log.debug("[{}] git gc successful", projectName); - } - - @Override - public void deleteIncomingPacks() throws IOException { - Log.debug( - "[{}] Checking for garbage `incoming` files", - projectName - ); - Files.walkFileTree(getDotGitDir().toPath(), new FileVisitor() { - @Override - public FileVisitResult preVisitDirectory( - Path dir, - BasicFileAttributes attrs - ) throws IOException { - return FileVisitResult.CONTINUE; + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + File file_ = file.toFile(); + String name = file_.getName(); + if (name.startsWith("incoming_") && name.endsWith(".pack")) { + Log.debug("Deleting garbage `incoming` file: {}", file_); + Preconditions.checkState(file_.delete()); } + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult visitFile( - Path file, - BasicFileAttributes attrs - ) throws IOException { - File file_ = file.toFile(); - String name = file_.getName(); - if (name.startsWith("incoming_") && name.endsWith(".pack")) { - Log.debug("Deleting garbage `incoming` file: {}", file_); - Preconditions.checkState(file_.delete()); - } - return FileVisitResult.CONTINUE; + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + Preconditions.checkNotNull(file); + Preconditions.checkNotNull(exc); + Log.warn("Failed to visit file: " + file, exc); + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Preconditions.checkNotNull(dir); + if (exc != null) { + return FileVisitResult.TERMINATE; } - - @Override - public FileVisitResult visitFileFailed( - Path file, - IOException exc - ) throws IOException { - Preconditions.checkNotNull(file); - Preconditions.checkNotNull(exc); - Log.warn("Failed to visit file: " + file, exc); - return FileVisitResult.TERMINATE; - } - - @Override - public FileVisitResult postVisitDirectory( - Path dir, - IOException exc - ) throws IOException { - Preconditions.checkNotNull(dir); - if (exc != null) { - return FileVisitResult.TERMINATE; - } - return FileVisitResult.CONTINUE; - } - + return FileVisitResult.CONTINUE; + } }); - } + } - @Override - public File getProjectDir() { - return getJGitRepository().getDirectory().getParentFile(); - } + @Override + public File getProjectDir() { + return getJGitRepository().getDirectory().getParentFile(); + } - public void resetHard() throws IOException { - Git git = new Git(getJGitRepository()); - try { - git.reset().setMode(ResetCommand.ResetType.HARD).call(); - } catch (GitAPIException e) { - throw new IOException(e); - } + public void resetHard() throws IOException { + Git git = new Git(getJGitRepository()); + try { + git.reset().setMode(ResetCommand.ResetType.HARD).call(); + } catch (GitAPIException e) { + throw new IOException(e); } + } - @Override - public Repository getJGitRepository() { - return repository.get(); + @Override + public Repository getJGitRepository() { + return repository.get(); + } + + public File getDotGitDir() { + return getJGitRepository().getWorkTree(); + } + + private void initRepositoryField(RepoStore repoStore) throws IOException { + Preconditions.checkNotNull(repoStore); + Preconditions.checkArgument(Project.isValidProjectName(projectName)); + Preconditions.checkState(!repository.isPresent()); + repository = Optional.of(createJGitRepository(repoStore, projectName)); + } + + private Repository createJGitRepository(RepoStore repoStore, String projName) throws IOException { + File repoDir = new File(repoStore.getRootDirectory(), projName); + return new FileRepositoryBuilder().setWorkTree(repoDir).build(); + } + + private Collection doCommitAndGetMissing(GitDirectoryContents contents) + throws IOException, GitAPIException { + Preconditions.checkState(repository.isPresent()); + Repository repo = getJGitRepository(); + resetHard(); + String name = getProjectName(); + Log.debug("[{}] Writing commit", name); + contents.write(); + Git git = new Git(getJGitRepository()); + Log.debug("[{}] Getting missing files", name); + Set missingFiles = git.status().call().getMissing(); + for (String missing : missingFiles) { + Log.debug("[{}] Git rm {}", name, missing); + git.rm().setCached(true).addFilepattern(missing).call(); } - - public File getDotGitDir() { - return getJGitRepository().getWorkTree(); - } - - private void initRepositoryField(RepoStore repoStore) throws IOException { - Preconditions.checkNotNull(repoStore); - Preconditions.checkArgument(Project.isValidProjectName(projectName)); - Preconditions.checkState(!repository.isPresent()); - repository = Optional.of(createJGitRepository(repoStore, projectName)); - } - - private Repository createJGitRepository( - RepoStore repoStore, - String projName - ) throws IOException { - File repoDir = new File(repoStore.getRootDirectory(), projName); - return new FileRepositoryBuilder().setWorkTree(repoDir).build(); - } - - private Collection doCommitAndGetMissing( - GitDirectoryContents contents - ) throws IOException, GitAPIException { - Preconditions.checkState(repository.isPresent()); - Repository repo = getJGitRepository(); - resetHard(); - String name = getProjectName(); - Log.debug("[{}] Writing commit", name); - contents.write(); - Git git = new Git(getJGitRepository()); - Log.debug("[{}] Getting missing files", name); - Set missingFiles = git.status().call().getMissing(); - for (String missing : missingFiles) { - Log.debug("[{}] Git rm {}", name, missing); - git.rm().setCached(true).addFilepattern(missing).call(); - } - Log.debug("[{}] Calling Git add", name); - git.add( - ).setWorkingTreeIterator( - new NoGitignoreIterator(repo) - ).addFilepattern(".").call(); - Log.debug("[{}] Calling Git commit", name); - git.commit( - ).setAuthor( - new PersonIdent( - contents.getUserName(), - contents.getUserEmail(), - contents.getWhen(), - TimeZone.getDefault() - ) - ).setMessage( - contents.getCommitMessage() - ).call(); - Log.debug( - "[{}] Deleting files in directory: {}", - name, - contents.getDirectory().getAbsolutePath() - ); - Util.deleteInDirectoryApartFrom(contents.getDirectory(), ".git"); - return missingFiles; - } - + Log.debug("[{}] Calling Git add", name); + git.add().setWorkingTreeIterator(new NoGitignoreIterator(repo)).addFilepattern(".").call(); + Log.debug("[{}] Calling Git commit", name); + git.commit() + .setAuthor( + new PersonIdent( + contents.getUserName(), + contents.getUserEmail(), + contents.getWhen(), + TimeZone.getDefault())) + .setMessage(contents.getCommitMessage()) + .call(); + Log.debug( + "[{}] Deleting files in directory: {}", name, contents.getDirectory().getAbsolutePath()); + Util.deleteInDirectoryApartFrom(contents.getDirectory(), ".git"); + return missingFiles; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/NoGitignoreIterator.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/NoGitignoreIterator.java index 64103c075e..437fdc1747 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/NoGitignoreIterator.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/NoGitignoreIterator.java @@ -1,5 +1,7 @@ package uk.ac.ic.wlgitbridge.bridge.repo; +import java.io.File; +import java.lang.reflect.Field; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.FileTreeIterator; @@ -7,81 +9,65 @@ import org.eclipse.jgit.treewalk.WorkingTreeIterator; import org.eclipse.jgit.treewalk.WorkingTreeOptions; import org.eclipse.jgit.util.FS; -import java.io.File; -import java.lang.reflect.Field; - -/** +/* * Created by winston on 08/10/2016. */ public class NoGitignoreIterator extends FileTreeIterator { - private static final Field ignoreNodeField; + private static final Field ignoreNodeField; - static { - try { - ignoreNodeField = WorkingTreeIterator.class.getDeclaredField( - "ignoreNode" - ); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - ignoreNodeField.setAccessible(true); + static { + try { + ignoreNodeField = WorkingTreeIterator.class.getDeclaredField("ignoreNode"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); } + ignoreNodeField.setAccessible(true); + } - public NoGitignoreIterator(Repository repo) { - super(repo); - } + public NoGitignoreIterator(Repository repo) { + super(repo); + } - public NoGitignoreIterator( - Repository repo, - FileModeStrategy fileModeStrategy - ) { - super(repo, fileModeStrategy); - } + public NoGitignoreIterator(Repository repo, FileModeStrategy fileModeStrategy) { + super(repo, fileModeStrategy); + } - public NoGitignoreIterator(File root, FS fs, WorkingTreeOptions options) { - super(root, fs, options); - } + public NoGitignoreIterator(File root, FS fs, WorkingTreeOptions options) { + super(root, fs, options); + } - public NoGitignoreIterator( - File root, - FS fs, - WorkingTreeOptions options, - FileModeStrategy fileModeStrategy - ) { - super(root, fs, options, fileModeStrategy); - } + public NoGitignoreIterator( + File root, FS fs, WorkingTreeOptions options, FileModeStrategy fileModeStrategy) { + super(root, fs, options, fileModeStrategy); + } - protected NoGitignoreIterator(FileTreeIterator p, File root, FS fs) { - super(p, root, fs); - } + protected NoGitignoreIterator(FileTreeIterator p, File root, FS fs) { + super(p, root, fs); + } - protected NoGitignoreIterator( - WorkingTreeIterator p, - File root, - FS fs, - FileModeStrategy fileModeStrategy - ) { - super(p, root, fs, fileModeStrategy); - } + protected NoGitignoreIterator( + WorkingTreeIterator p, File root, FS fs, FileModeStrategy fileModeStrategy) { + super(p, root, fs, fileModeStrategy); + } - // Note: the `list` is a list of top-level entities in this directory, - // not a full list of files in the tree. - @Override - protected void init(Entry[] list) { - super.init(list); - try { - ignoreNodeField.set(this, null); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } + // Note: the `list` is a list of top-level entities in this directory, + // not a full list of files in the tree. + @Override + protected void init(Entry[] list) { + super.init(list); + try { + ignoreNodeField.set(this, null); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); } + } - // When entering a sub-directory, create a new instance of this class, - // so we can also ignore gitignore specifications in sub-directories - @Override - protected AbstractTreeIterator enterSubtree() { - String fullPath = getDirectory().getAbsolutePath() + "/" + current().getName(); - return new NoGitignoreIterator(this, new File(fullPath), fs, fileModeStrategy); - } + // When entering a sub-directory, create a new instance of this class, + // so we can also ignore gitignore specifications in sub-directories + @Override + protected AbstractTreeIterator enterSubtree() { + String fullPath = getDirectory().getAbsolutePath() + "/" + current().getName(); + return new NoGitignoreIterator(this, new File(fullPath), fs, fileModeStrategy); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/ProjectRepo.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/ProjectRepo.java index 1e77d45010..5d82e3dd83 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/ProjectRepo.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/ProjectRepo.java @@ -1,41 +1,34 @@ package uk.ac.ic.wlgitbridge.bridge.repo; +import java.io.File; +import java.io.IOException; +import java.util.Collection; import org.eclipse.jgit.lib.Repository; import uk.ac.ic.wlgitbridge.data.filestore.GitDirectoryContents; import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; -import java.io.File; -import java.io.IOException; -import java.util.Collection; - -/** +/* * Created by winston on 20/08/2016. */ public interface ProjectRepo { - String getProjectName(); + String getProjectName(); - void initRepo( - RepoStore repoStore - ) throws IOException; + void initRepo(RepoStore repoStore) throws IOException; - void useExistingRepository( - RepoStore repoStore - ) throws IOException; + void useExistingRepository(RepoStore repoStore) throws IOException; - RawDirectory getDirectory( - ) throws IOException, GitUserException; + RawDirectory getDirectory() throws IOException, GitUserException; - Collection commitAndGetMissing( - GitDirectoryContents gitDirectoryContents - ) throws IOException, GitUserException; + Collection commitAndGetMissing(GitDirectoryContents gitDirectoryContents) + throws IOException, GitUserException; - void runGC() throws IOException; + void runGC() throws IOException; - void deleteIncomingPacks() throws IOException; + void deleteIncomingPacks() throws IOException; - File getProjectDir(); + File getProjectDir(); - Repository getJGitRepository(); + Repository getJGitRepository(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStore.java index 23bd294693..63ee3d1462 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStore.java @@ -1,102 +1,82 @@ package uk.ac.ic.wlgitbridge.bridge.repo; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Repository; - import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collection; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; -/** +/* * Created by winston on 20/08/2016. */ public interface RepoStore { - /* Still need to get rid of these two methods. - Main dependency: GitRepoStore needs a Repository which needs a directory. - Instead, use a visitor or something. */ - String getRepoStorePath(); + /* Still need to get rid of these two methods. + Main dependency: GitRepoStore needs a Repository which needs a directory. + Instead, use a visitor or something. */ + String getRepoStorePath(); - File getRootDirectory(); + File getRootDirectory(); - ProjectRepo initRepo(String project) throws IOException; + ProjectRepo initRepo(String project) throws IOException; - ProjectRepo getExistingRepo(String project) throws IOException; + ProjectRepo getExistingRepo(String project) throws IOException; - ProjectRepo useJGitRepo(Repository repo, ObjectId commitId); + ProjectRepo useJGitRepo(Repository repo, ObjectId commitId); - void purgeNonexistentProjects( - Collection existingProjectNames - ); + void purgeNonexistentProjects(Collection existingProjectNames); - long totalSize(); + long totalSize(); - /** - * Tars and bzip2s the .git directory of the given project. Throws an - * IOException if the project doesn't exist. The returned stream is a copy - * of the original .git directory, which must be deleted using remove(). - */ - InputStream bzip2Project( - String projectName, - long[] sizePtr - ) throws IOException; + /* + * Tars and bzip2s the .git directory of the given project. Throws an + * IOException if the project doesn't exist. The returned stream is a copy + * of the original .git directory, which must be deleted using remove(). + */ + InputStream bzip2Project(String projectName, long[] sizePtr) throws IOException; - default InputStream bzip2Project( - String projectName - ) throws IOException { - return bzip2Project(projectName, null); - } + default InputStream bzip2Project(String projectName) throws IOException { + return bzip2Project(projectName, null); + } - /** - * Tars and gzips the .git directory of the given project. Throws an - * IOException if the project doesn't exist. The returned stream is a copy - * of the original .git directory, which must be deleted using remove(). - */ - InputStream gzipProject( - String projectName, - long[] sizePtr - ) throws IOException; + /* + * Tars and gzips the .git directory of the given project. Throws an + * IOException if the project doesn't exist. The returned stream is a copy + * of the original .git directory, which must be deleted using remove(). + */ + InputStream gzipProject(String projectName, long[] sizePtr) throws IOException; - default InputStream gzipProject( - String projectName - ) throws IOException { - return gzipProject(projectName, null); - } + default InputStream gzipProject(String projectName) throws IOException { + return gzipProject(projectName, null); + } - void gcProject(String projectName) throws IOException; + void gcProject(String projectName) throws IOException; - /** - * Called after {@link #bzip2Project(String, long[])}'s has been safely - * uploaded to the swap store. Removes all traces of the project from disk, - * i.e. not just its .git, but the whole project's git directory. - * @param projectName - * @throws IOException - */ - void remove(String projectName) throws IOException; + /* + * Called after {@link #bzip2Project(String, long[])}'s has been safely + * uploaded to the swap store. Removes all traces of the project from disk, + * i.e. not just its .git, but the whole project's git directory. + * @param projectName + * @throws IOException + */ + void remove(String projectName) throws IOException; - /** - * Unbzip2s the given data stream into a .git directory for projectName. - * Creates the project's git directory. - * If projectName already exists, throws an IOException. - * @param projectName the name of the project, e.g. abc123 - * @param dataStream the data stream containing the bzipped contents. - */ - void unbzip2Project( - String projectName, - InputStream dataStream - ) throws IOException; - - /** - * Ungzips the given data stream into a .git directory for projectName. - * Creates the project's git directory. - * If projectName already exists, throws an IOException. - * @param projectName the name of the project, e.g. abc123 - * @param dataStream the data stream containing the gzip contents. - */ - void ungzipProject( - String projectName, - InputStream dataStream - ) throws IOException; + /* + * Unbzip2s the given data stream into a .git directory for projectName. + * Creates the project's git directory. + * If projectName already exists, throws an IOException. + * @param projectName the name of the project, e.g. abc123 + * @param dataStream the data stream containing the bzipped contents. + */ + void unbzip2Project(String projectName, InputStream dataStream) throws IOException; + /* + * Ungzips the given data stream into a .git directory for projectName. + * Creates the project's git directory. + * If projectName already exists, throws an IOException. + * @param projectName the name of the project, e.g. abc123 + * @param dataStream the data stream containing the gzip contents. + */ + void ungzipProject(String projectName, InputStream dataStream) throws IOException; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStoreConfig.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStoreConfig.java index 52d41104a1..feb01d3ded 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStoreConfig.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/RepoStoreConfig.java @@ -1,29 +1,27 @@ package uk.ac.ic.wlgitbridge.bridge.repo; -import javax.annotation.Nullable; import java.util.Optional; +import javax.annotation.Nullable; -/** +/* * Created by winston on 02/07/2017. */ public class RepoStoreConfig { - @Nullable - private final Long maxFileSize; + @Nullable private final Long maxFileSize; - @Nullable - private final Long maxFileNum; + @Nullable private final Long maxFileNum; - public RepoStoreConfig(Long maxFileSize, Long maxFileNum) { - this.maxFileSize = maxFileSize; - this.maxFileNum = maxFileNum; - } + public RepoStoreConfig(Long maxFileSize, Long maxFileNum) { + this.maxFileSize = maxFileSize; + this.maxFileNum = maxFileNum; + } - public Optional getMaxFileSize() { - return Optional.ofNullable(maxFileSize); - } + public Optional getMaxFileSize() { + return Optional.ofNullable(maxFileSize); + } - public Optional getMaxFileNum() { - return Optional.ofNullable(maxFileNum); - } + public Optional getMaxFileNum() { + return Optional.ofNullable(maxFileNum); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/WalkOverrideGitRepo.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/WalkOverrideGitRepo.java index c240001c7c..ded4269ff3 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/WalkOverrideGitRepo.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/repo/WalkOverrideGitRepo.java @@ -1,5 +1,9 @@ package uk.ac.ic.wlgitbridge.bridge.repo; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Optional; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import uk.ac.ic.wlgitbridge.data.filestore.GitDirectoryContents; @@ -7,12 +11,7 @@ import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; import uk.ac.ic.wlgitbridge.git.util.RepositoryObjectTreeWalker; -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.Optional; - -/** +/* * This class takes a GitProjectRepo and delegates all calls to it. * * The purpose is to insert a file size check in {@link #getDirectory()}. @@ -22,74 +21,69 @@ import java.util.Optional; */ public class WalkOverrideGitRepo implements ProjectRepo { - private final GitProjectRepo gitRepo; + private final GitProjectRepo gitRepo; - private final Optional maxFileSize; + private final Optional maxFileSize; - private final Optional commitId; + private final Optional commitId; - public WalkOverrideGitRepo( - GitProjectRepo gitRepo, - Optional maxFileSize, - Optional commitId - ) { - this.gitRepo = gitRepo; - this.maxFileSize = maxFileSize; - this.commitId = commitId; + public WalkOverrideGitRepo( + GitProjectRepo gitRepo, Optional maxFileSize, Optional commitId) { + this.gitRepo = gitRepo; + this.maxFileSize = maxFileSize; + this.commitId = commitId; + } + + @Override + public String getProjectName() { + return gitRepo.getProjectName(); + } + + @Override + public void initRepo(RepoStore repoStore) throws IOException { + gitRepo.initRepo(repoStore); + } + + @Override + public void useExistingRepository(RepoStore repoStore) throws IOException { + gitRepo.useExistingRepository(repoStore); + } + + @Override + public RawDirectory getDirectory() throws IOException, GitUserException { + Repository repo = gitRepo.getJGitRepository(); + RepositoryObjectTreeWalker walker; + if (commitId.isPresent()) { + walker = new RepositoryObjectTreeWalker(repo, commitId.get()); + } else { + walker = new RepositoryObjectTreeWalker(repo); } + return walker.getDirectoryContents(maxFileSize); + } - @Override - public String getProjectName() { - return gitRepo.getProjectName(); - } + @Override + public Collection commitAndGetMissing(GitDirectoryContents gitDirectoryContents) + throws GitUserException, IOException { + return gitRepo.commitAndGetMissing(gitDirectoryContents); + } - @Override - public void initRepo(RepoStore repoStore) throws IOException { - gitRepo.initRepo(repoStore); - } + @Override + public void runGC() throws IOException { + gitRepo.runGC(); + } - @Override - public void useExistingRepository(RepoStore repoStore) throws IOException { - gitRepo.useExistingRepository(repoStore); - } + @Override + public void deleteIncomingPacks() throws IOException { + gitRepo.deleteIncomingPacks(); + } - @Override - public RawDirectory getDirectory() throws IOException, GitUserException { - Repository repo = gitRepo.getJGitRepository(); - RepositoryObjectTreeWalker walker; - if (commitId.isPresent()) { - walker = new RepositoryObjectTreeWalker(repo, commitId.get()); - } else { - walker = new RepositoryObjectTreeWalker(repo); - } - return walker.getDirectoryContents(maxFileSize); - } - - @Override - public Collection commitAndGetMissing( - GitDirectoryContents gitDirectoryContents - ) throws GitUserException, IOException { - return gitRepo.commitAndGetMissing(gitDirectoryContents); - } - - @Override - public void runGC() throws IOException { - gitRepo.runGC(); - } - - @Override - public void deleteIncomingPacks() throws IOException { - gitRepo.deleteIncomingPacks(); - } - - @Override - public File getProjectDir() { - return gitRepo.getProjectDir(); - } - - @Override - public Repository getJGitRepository() { - return gitRepo.getJGitRepository(); - } + @Override + public File getProjectDir() { + return gitRepo.getProjectDir(); + } + @Override + public Repository getJGitRepository() { + return gitRepo.getJGitRepository(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/ResourceCache.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/ResourceCache.java index 7a520bb3f8..3bf568f74c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/ResourceCache.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/ResourceCache.java @@ -1,24 +1,22 @@ package uk.ac.ic.wlgitbridge.bridge.resource; -import uk.ac.ic.wlgitbridge.data.filestore.RawFile; -import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException; - import java.io.IOException; import java.util.Map; import java.util.Optional; +import uk.ac.ic.wlgitbridge.data.filestore.RawFile; +import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException; -/** +/* * Created by winston on 20/08/2016. */ public interface ResourceCache { - RawFile get( - String projectName, - String url, - String newPath, - Map fileTable, - Map fetchedUrls, - Optional maxFileSize - ) throws IOException, SizeLimitExceededException; - + RawFile get( + String projectName, + String url, + String newPath, + Map fileTable, + Map fetchedUrls, + Optional maxFileSize) + throws IOException, SizeLimitExceededException; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCache.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCache.java index 29d218d3f9..d01027d2a2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCache.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCache.java @@ -1,6 +1,12 @@ package uk.ac.ic.wlgitbridge.bridge.resource; import static org.asynchttpclient.Dsl.*; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; import uk.ac.ic.wlgitbridge.bridge.db.DBStore; import uk.ac.ic.wlgitbridge.data.filestore.RawFile; import uk.ac.ic.wlgitbridge.data.filestore.RepositoryFile; @@ -10,133 +16,124 @@ import uk.ac.ic.wlgitbridge.io.http.ning.NingHttpClientFacade; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Log; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -/** +/* * Created by winston on 20/08/2016. */ public class UrlResourceCache implements ResourceCache { - private final DBStore dbStore; + private final DBStore dbStore; - private final NingHttpClientFacade http; + private final NingHttpClientFacade http; - UrlResourceCache(DBStore dbStore, NingHttpClientFacade http) { - this.dbStore = dbStore; - this.http = http; - } + UrlResourceCache(DBStore dbStore, NingHttpClientFacade http) { + this.dbStore = dbStore; + this.http = http; + } - public UrlResourceCache(DBStore dbStore) { - this(dbStore, new NingHttpClient(asyncHttpClient())); - } + public UrlResourceCache(DBStore dbStore) { + this(dbStore, new NingHttpClient(asyncHttpClient())); + } - @Override - public RawFile get( - String projectName, - String url, - String newPath, - Map fileTable, - Map fetchedUrls, - Optional maxFileSize - ) throws IOException, SizeLimitExceededException { - String path = dbStore.getPathForURLInProject(projectName, getCacheKeyFromUrl(url)); - byte[] contents; - if (path == null) { - path = newPath; - contents = fetch(projectName, url, path, maxFileSize); - fetchedUrls.put(url, contents); + @Override + public RawFile get( + String projectName, + String url, + String newPath, + Map fileTable, + Map fetchedUrls, + Optional maxFileSize) + throws IOException, SizeLimitExceededException { + String path = dbStore.getPathForURLInProject(projectName, getCacheKeyFromUrl(url)); + byte[] contents; + if (path == null) { + path = newPath; + contents = fetch(projectName, url, path, maxFileSize); + fetchedUrls.put(url, contents); + } else { + Log.debug("Found (" + projectName + "): " + url); + Log.debug("At (" + projectName + "): " + path); + contents = fetchedUrls.get(url); + if (contents == null) { + RawFile rawFile = fileTable.get(path); + if (rawFile == null) { + Log.warn( + "File " + + path + + " was not in the current commit, " + + "or the git tree, yet path was not null. " + + "File url is: " + + url); + contents = fetch(projectName, url, path, maxFileSize); } else { - Log.debug("Found (" + projectName + "): " + url); - Log.debug("At (" + projectName + "): " + path); - contents = fetchedUrls.get(url); - if (contents == null) { - RawFile rawFile = fileTable.get(path); - if (rawFile == null) { - Log.warn( - "File " + path - + " was not in the current commit, " - + "or the git tree, yet path was not null. " - + "File url is: " - + url - ); - contents = fetch(projectName, url, path, maxFileSize); - } else { - contents = rawFile.getContents(); - } - } + contents = rawFile.getContents(); } - return new RepositoryFile(newPath, contents); + } } + return new RepositoryFile(newPath, contents); + } - private byte[] fetch( - String projectName, - final String url, - String path, - Optional maxFileSize - ) throws FailedConnectionException, SizeLimitExceededException { - byte[] contents; - Log.debug("GET -> " + url); - try { - contents = http.get(url, hs -> { + private byte[] fetch( + String projectName, final String url, String path, Optional maxFileSize) + throws FailedConnectionException, SizeLimitExceededException { + byte[] contents; + Log.debug("GET -> " + url); + try { + contents = + http.get( + url, + hs -> { List contentLengths = hs.getAll("Content-Length"); if (!maxFileSize.isPresent()) { - return true; + return true; } if (contentLengths.isEmpty()) { - return true; + return true; } long contentLength = Long.parseLong(contentLengths.get(0)); long maxFileSize_ = maxFileSize.get(); if (contentLength <= maxFileSize_) { - return true; + return true; } throw new SizeLimitExceededException( - Optional.of(path), contentLength, maxFileSize_ - ); - }); - } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof SizeLimitExceededException) { - throw (SizeLimitExceededException) cause; - } - Log.warn( - "ExecutionException when fetching project: " + - projectName + - ", url: " + - url + - ", path: " + - path, - e - ); - throw new FailedConnectionException(); - } - if (maxFileSize.isPresent() && contents.length > maxFileSize.get()) { - throw new SizeLimitExceededException( - Optional.of(path), contents.length, maxFileSize.get()); - } - dbStore.addURLIndexForProject(projectName, getCacheKeyFromUrl(url), path); - return contents; + Optional.of(path), contentLength, maxFileSize_); + }); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof SizeLimitExceededException) { + throw (SizeLimitExceededException) cause; + } + Log.warn( + "ExecutionException when fetching project: " + + projectName + + ", url: " + + url + + ", path: " + + path, + e); + throw new FailedConnectionException(); } + if (maxFileSize.isPresent() && contents.length > maxFileSize.get()) { + throw new SizeLimitExceededException(Optional.of(path), contents.length, maxFileSize.get()); + } + dbStore.addURLIndexForProject(projectName, getCacheKeyFromUrl(url), path); + return contents; + } - /** - * Construct a suitable cache key from the given file URL. - * - * The file URL returned by the web service may contain a token parameter - * used for authentication. This token changes for every request, so we - * need to strip it from the query string before using the URL as a cache - * key. - */ - private String getCacheKeyFromUrl(String url) { - // We're not doing proper URL parsing here, but it should be enough to - // remove the token without touching the important parts of the URL. - // - // The URL looks like: - // - // https://history.overleaf.com/api/projects/:project_id/blobs/:hash?token=:token&_path=:path - return url.replaceAll("token=[^&]*", "token=REMOVED"); - } + /* + * Construct a suitable cache key from the given file URL. + * + * The file URL returned by the web service may contain a token parameter + * used for authentication. This token changes for every request, so we + * need to strip it from the query string before using the URL as a cache + * key. + */ + private String getCacheKeyFromUrl(String url) { + // We're not doing proper URL parsing here, but it should be enough to + // remove the token without touching the important parts of the URL. + // + // The URL looks like: + // + // https://history.overleaf.com/api/projects/:project_id/blobs/:hash?token=:token&_path=:path + return url.replaceAll("token=[^&]*", "token=REMOVED"); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/NetSnapshotApi.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/NetSnapshotApi.java index c289e44c30..b8f2636662 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/NetSnapshotApi.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/NetSnapshotApi.java @@ -1,6 +1,8 @@ package uk.ac.ic.wlgitbridge.bridge.snapshot; import com.google.api.client.auth.oauth2.Credential; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; import uk.ac.ic.wlgitbridge.data.CandidateSnapshot; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocRequest; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; @@ -11,45 +13,35 @@ import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.GetSavedVersResult; import uk.ac.ic.wlgitbridge.snapshot.push.PushRequest; import uk.ac.ic.wlgitbridge.snapshot.push.PushResult; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -/** +/* * Created by winston on 20/08/2016. */ public class NetSnapshotApi implements SnapshotApi { - @Override - public CompletableFuture getDoc( - Optional oauth2, String projectName) { - return new GetDocRequest(opt(oauth2), projectName).request(); - } + @Override + public CompletableFuture getDoc(Optional oauth2, String projectName) { + return new GetDocRequest(opt(oauth2), projectName).request(); + } - @Override - public CompletableFuture getForVersion( - Optional oauth2, String projectName, int versionId) { - return new GetForVersionRequest( - opt(oauth2), projectName, versionId).request(); - } + @Override + public CompletableFuture getForVersion( + Optional oauth2, String projectName, int versionId) { + return new GetForVersionRequest(opt(oauth2), projectName, versionId).request(); + } - @Override - public CompletableFuture getSavedVers( - Optional oauth2, String projectName) { - return new GetSavedVersRequest(opt(oauth2), projectName).request(); - } + @Override + public CompletableFuture getSavedVers( + Optional oauth2, String projectName) { + return new GetSavedVersRequest(opt(oauth2), projectName).request(); + } - @Override - public CompletableFuture push( - Optional oauth2, - CandidateSnapshot candidateSnapshot, - String postbackKey - ) { - return new PushRequest( - opt(oauth2), candidateSnapshot, postbackKey).request(); - } - - private static Credential opt(Optional oauth2) { - return oauth2.orElse(null); - } + @Override + public CompletableFuture push( + Optional oauth2, CandidateSnapshot candidateSnapshot, String postbackKey) { + return new PushRequest(opt(oauth2), candidateSnapshot, postbackKey).request(); + } + private static Credential opt(Optional oauth2) { + return oauth2.orElse(null); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApi.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApi.java index 0358db7834..67fec434d4 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApi.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApi.java @@ -1,54 +1,49 @@ package uk.ac.ic.wlgitbridge.bridge.snapshot; import com.google.api.client.auth.oauth2.Credential; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import uk.ac.ic.wlgitbridge.data.CandidateSnapshot; -import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException; +import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; import uk.ac.ic.wlgitbridge.snapshot.getforversion.GetForVersionResult; import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.GetSavedVersResult; import uk.ac.ic.wlgitbridge.snapshot.push.PushResult; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; - -/** +/* * Created by winston on 20/08/2016. */ public interface SnapshotApi { - CompletableFuture getDoc( - Optional oauth2, String projectName); + CompletableFuture getDoc(Optional oauth2, String projectName); - CompletableFuture getForVersion( - Optional oauth2, String projectName, int versionId); + CompletableFuture getForVersion( + Optional oauth2, String projectName, int versionId); - CompletableFuture getSavedVers( - Optional oauth2, String projectName); + CompletableFuture getSavedVers( + Optional oauth2, String projectName); - CompletableFuture push( - Optional oauth2, - CandidateSnapshot candidateSnapshot, - String postbackKey); + CompletableFuture push( + Optional oauth2, CandidateSnapshot candidateSnapshot, String postbackKey); - static T getResult(CompletableFuture result) - throws MissingRepositoryException, FailedConnectionException, ForbiddenException { - try { - return result.join(); - } catch (CompletionException e) { - try { - throw e.getCause(); - } catch (MissingRepositoryException - | FailedConnectionException - | ForbiddenException - | RuntimeException r) { - throw r; - } catch (Throwable __) { - throw e; - } - } + static T getResult(CompletableFuture result) + throws MissingRepositoryException, FailedConnectionException, ForbiddenException { + try { + return result.join(); + } catch (CompletionException e) { + try { + throw e.getCause(); + } catch (MissingRepositoryException + | FailedConnectionException + | ForbiddenException + | RuntimeException r) { + throw r; + } catch (Throwable __) { + throw e; + } } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApiFacade.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApiFacade.java index 9aa3165e06..07ac0a9a40 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApiFacade.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/snapshot/SnapshotApiFacade.java @@ -1,11 +1,14 @@ package uk.ac.ic.wlgitbridge.bridge.snapshot; import com.google.api.client.auth.oauth2.Credential; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import uk.ac.ic.wlgitbridge.data.CandidateSnapshot; import uk.ac.ic.wlgitbridge.data.model.Snapshot; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; -import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException; +import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; import uk.ac.ic.wlgitbridge.snapshot.getforversion.GetForVersionResult; @@ -15,148 +18,104 @@ import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.SnapshotInfo; import uk.ac.ic.wlgitbridge.snapshot.push.PushResult; import uk.ac.ic.wlgitbridge.snapshot.push.exception.InvalidProjectException; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -/** +/* * Created by winston on 02/07/2017. */ public class SnapshotApiFacade { - private final SnapshotApi api; + private final SnapshotApi api; - public SnapshotApiFacade(SnapshotApi api) { - this.api = api; + public SnapshotApiFacade(SnapshotApi api) { + this.api = api; + } + + public boolean projectExists(Optional oauth2, String projectName) + throws FailedConnectionException, GitUserException { + try { + SnapshotApi.getResult(api.getDoc(oauth2, projectName)).getVersionID(); + return true; + } catch (InvalidProjectException e) { + return false; } + } - public boolean projectExists( - Optional oauth2, - String projectName - ) throws FailedConnectionException, GitUserException { - try { - SnapshotApi - .getResult(api.getDoc(oauth2, projectName)) - .getVersionID(); - return true; - } catch (InvalidProjectException e) { - return false; + public Optional getDoc(Optional oauth2, String projectName) + throws FailedConnectionException, GitUserException { + try { + GetDocResult doc = SnapshotApi.getResult(api.getDoc(oauth2, projectName)); + doc.getVersionID(); + return Optional.of(doc); + } catch (InvalidProjectException e) { + return Optional.empty(); + } + } + + public Deque getSnapshots( + Optional oauth2, String projectName, int afterVersionId) + throws GitUserException, FailedConnectionException { + List snapshotInfos = + getSnapshotInfosAfterVersion(oauth2, projectName, afterVersionId); + List snapshotDatas = getMatchingSnapshotData(oauth2, projectName, snapshotInfos); + return combine(snapshotInfos, snapshotDatas); + } + + public PushResult push( + Optional oauth2, CandidateSnapshot candidateSnapshot, String postbackKey) + throws MissingRepositoryException, FailedConnectionException, ForbiddenException { + return SnapshotApi.getResult(api.push(oauth2, candidateSnapshot, postbackKey)); + } + + private List getSnapshotInfosAfterVersion( + Optional oauth2, String projectName, int version) + throws FailedConnectionException, GitUserException { + SortedSet versions = new TreeSet<>(); + CompletableFuture getDoc = api.getDoc(oauth2, projectName); + CompletableFuture savedVers = api.getSavedVers(oauth2, projectName); + GetDocResult latestDoc = SnapshotApi.getResult(getDoc); + int latest = latestDoc.getVersionID(); + // Handle edge-case for projects with no changes, that were imported + // to v2. In which case both `latest` and `version` will be zero. + // See: https://github.com/overleaf/writelatex-git-bridge/pull/50 + if (latest > version || (latest == 0 && version == 0)) { + for (SnapshotInfo snapshotInfo : SnapshotApi.getResult(savedVers).getSavedVers()) { + if (snapshotInfo.getVersionId() > version) { + versions.add(snapshotInfo); } + } + versions.add( + new SnapshotInfo( + latest, latestDoc.getCreatedAt(), latestDoc.getName(), latestDoc.getEmail())); } + return new ArrayList<>(versions); + } - public Optional getDoc( - Optional oauth2, - String projectName - ) throws FailedConnectionException, GitUserException { - try { - GetDocResult doc = SnapshotApi - .getResult(api.getDoc(oauth2, projectName)); - doc.getVersionID(); - return Optional.of(doc); - } catch (InvalidProjectException e) { - return Optional.empty(); - } + private List getMatchingSnapshotData( + Optional oauth2, String projectName, List snapshotInfos) + throws FailedConnectionException, ForbiddenException { + List> firedRequests = + fireDataRequests(oauth2, projectName, snapshotInfos); + List snapshotDataList = new ArrayList<>(); + for (CompletableFuture fired : firedRequests) { + snapshotDataList.add(fired.join().getSnapshotData()); } + return snapshotDataList; + } - public Deque getSnapshots( - Optional oauth2, - String projectName, - int afterVersionId - ) throws GitUserException, FailedConnectionException { - List snapshotInfos = getSnapshotInfosAfterVersion( - oauth2, - projectName, - afterVersionId - ); - List snapshotDatas = getMatchingSnapshotData( - oauth2, - projectName, - snapshotInfos - ); - return combine(snapshotInfos, snapshotDatas); + private List> fireDataRequests( + Optional oauth2, String projectName, List snapshotInfos) { + return snapshotInfos.stream() + .map(snap -> api.getForVersion(oauth2, projectName, snap.getVersionId())) + .collect(Collectors.toList()); + } + + private static Deque combine( + List snapshotInfos, List snapshotDatas) { + Deque snapshots = new LinkedList<>(); + Iterator infos = snapshotInfos.iterator(); + Iterator datas = snapshotDatas.iterator(); + while (infos.hasNext()) { + snapshots.add(new Snapshot(infos.next(), datas.next())); } - - public PushResult push( - Optional oauth2, - CandidateSnapshot candidateSnapshot, - String postbackKey - ) throws MissingRepositoryException, FailedConnectionException, ForbiddenException { - return SnapshotApi.getResult(api.push( - oauth2, candidateSnapshot, postbackKey)); - } - - private List getSnapshotInfosAfterVersion( - Optional oauth2, - String projectName, - int version - ) throws FailedConnectionException, GitUserException { - SortedSet versions = new TreeSet<>(); - CompletableFuture getDoc - = api.getDoc(oauth2, projectName); - CompletableFuture savedVers - = api.getSavedVers(oauth2, projectName); - GetDocResult latestDoc = SnapshotApi.getResult(getDoc); - int latest = latestDoc.getVersionID(); - // Handle edge-case for projects with no changes, that were imported - // to v2. In which case both `latest` and `version` will be zero. - // See: https://github.com/overleaf/writelatex-git-bridge/pull/50 - if (latest > version || (latest == 0 && version == 0)) { - for ( - SnapshotInfo snapshotInfo : - SnapshotApi.getResult(savedVers).getSavedVers() - ) { - if (snapshotInfo.getVersionId() > version) { - versions.add(snapshotInfo); - } - } - versions.add(new SnapshotInfo( - latest, - latestDoc.getCreatedAt(), - latestDoc.getName(), - latestDoc.getEmail() - )); - - } - return new ArrayList<>(versions); - } - - private List getMatchingSnapshotData( - Optional oauth2, - String projectName, - List snapshotInfos - ) throws FailedConnectionException, ForbiddenException { - List> firedRequests - = fireDataRequests(oauth2, projectName, snapshotInfos); - List snapshotDataList = new ArrayList<>(); - for (CompletableFuture fired : firedRequests) { - snapshotDataList.add(fired.join().getSnapshotData()); - } - return snapshotDataList; - } - - private List> fireDataRequests( - Optional oauth2, - String projectName, - List snapshotInfos - ) { - return snapshotInfos - .stream() - .map(snap -> api.getForVersion( - oauth2, projectName, snap.getVersionId())) - .collect(Collectors.toList()); - } - - private static Deque combine( - List snapshotInfos, - List snapshotDatas - ) { - Deque snapshots = new LinkedList<>(); - Iterator infos = snapshotInfos.iterator(); - Iterator datas = snapshotDatas.iterator(); - while (infos.hasNext()) { - snapshots.add(new Snapshot(infos.next(), datas.next())); - } - return snapshots; - } - + return snapshots; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/NoopSwapJob.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/NoopSwapJob.java index 49ebf0c58b..977734a42c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/NoopSwapJob.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/NoopSwapJob.java @@ -1,20 +1,19 @@ package uk.ac.ic.wlgitbridge.bridge.swap.job; -/** +/* * Created by winston on 24/08/2016. */ public class NoopSwapJob implements SwapJob { - @Override - public void start() {} + @Override + public void start() {} - @Override - public void stop() {} + @Override + public void stop() {} - @Override - public void evict(String projName) {} - - @Override - public void restore(String projName) {} + @Override + public void evict(String projName) {} + @Override + public void restore(String projName) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJob.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJob.java index d671e50fc2..8afaf1721a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJob.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJob.java @@ -1,129 +1,124 @@ package uk.ac.ic.wlgitbridge.bridge.swap.job; +import java.io.IOException; +import java.util.Optional; import uk.ac.ic.wlgitbridge.bridge.db.DBStore; import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; import uk.ac.ic.wlgitbridge.bridge.swap.store.SwapStore; import uk.ac.ic.wlgitbridge.util.Log; -import java.io.IOException; -import java.util.Optional; - -/** +/* * Created by winston on 20/08/2016. */ public interface SwapJob { - enum CompressionMethod { Bzip2, Gzip } + enum CompressionMethod { + Bzip2, + Gzip + } - static CompressionMethod stringToCompressionMethod(String compressionString) { - if (compressionString == null) { - return null; - } - CompressionMethod result; - switch (compressionString) { - case "gzip": - result = CompressionMethod.Gzip; - break; - case "bzip2": - result = CompressionMethod.Bzip2; - break; - default: - result = null; - break; - } - return result; + static CompressionMethod stringToCompressionMethod(String compressionString) { + if (compressionString == null) { + return null; } - - static String compressionMethodAsString(CompressionMethod compressionMethod) { - if (compressionMethod == null) { - return null; - } - String result; - switch (compressionMethod) { - case Gzip: - result = "gzip"; - break; - case Bzip2: - result = "bzip2"; - break; - default: - result = null; - break; - } - return result; + CompressionMethod result; + switch (compressionString) { + case "gzip": + result = CompressionMethod.Gzip; + break; + case "bzip2": + result = CompressionMethod.Bzip2; + break; + default: + result = null; + break; } + return result; + } - static SwapJob fromConfig( - Optional cfg, - ProjectLock lock, - RepoStore repoStore, - DBStore dbStore, - SwapStore swapStore - ) { - if (!cfg.isPresent()) { - return new NoopSwapJob(); - } - if (!swapStore.isSafe() && !cfg.get().getAllowUnsafeStores()) { - Log.warn("Swap store '{}' is not safe; disabling swap job", swapStore.getClass().getSimpleName()); - return new NoopSwapJob(); - } - return new SwapJobImpl( - cfg.get(), - lock, - repoStore, - dbStore, - swapStore - ); + static String compressionMethodAsString(CompressionMethod compressionMethod) { + if (compressionMethod == null) { + return null; } + String result; + switch (compressionMethod) { + case Gzip: + result = "gzip"; + break; + case Bzip2: + result = "bzip2"; + break; + default: + result = null; + break; + } + return result; + } - /** - * Starts the swap job, which should schedule an attempted swap at the given - * configured interval (config["swapJob"]["intervalMillis"] - */ - void start(); + static SwapJob fromConfig( + Optional cfg, + ProjectLock lock, + RepoStore repoStore, + DBStore dbStore, + SwapStore swapStore) { + if (!cfg.isPresent()) { + return new NoopSwapJob(); + } + if (!swapStore.isSafe() && !cfg.get().getAllowUnsafeStores()) { + Log.warn( + "Swap store '{}' is not safe; disabling swap job", swapStore.getClass().getSimpleName()); + return new NoopSwapJob(); + } + return new SwapJobImpl(cfg.get(), lock, repoStore, dbStore, swapStore); + } - /** - * Stops the stop job. - */ - void stop(); + /* + * Starts the swap job, which should schedule an attempted swap at the given + * configured interval (config["swapJob"]["intervalMillis"] + */ + void start(); - /** - * Called by the swap job when a project should be evicted. - * - * Pre: - * 1. projName must be in repoStore - * 2. projName should not be in swapStore - * 3. projName should be PRESENT in dbStore (last_accessed is not null) - * - * Acquires the project lock and performs an eviction of projName. - * - * Post: - * 1. projName should not in repoStore - * 2. projName must be in swapStore - * 3. projName must be SWAPPED in dbStore (last_accessed is null) - * @param projName - * @throws IOException - */ - void evict(String projName) throws IOException; + /* + * Stops the stop job. + */ + void stop(); - /** - * Called on a project when it must be restored. - * - * Pre: - * 1. projName should not be in repoStore - * 2. projName must be in swapStore - * 3. projName must be SWAPPED in dbStore (last_accessed is null) - * - * Acquires the project lock and restores projName. - * - * Post: - * 1. projName must be in repoStore - * 2. projName should not in swapStore - * 3. projName should be PRESENT in dbStore (last_accessed is not null) - * @param projName - * @throws IOException - */ - void restore(String projName) throws IOException; + /* + * Called by the swap job when a project should be evicted. + * + * Pre: + * 1. projName must be in repoStore + * 2. projName should not be in swapStore + * 3. projName should be PRESENT in dbStore (last_accessed is not null) + * + * Acquires the project lock and performs an eviction of projName. + * + * Post: + * 1. projName should not in repoStore + * 2. projName must be in swapStore + * 3. projName must be SWAPPED in dbStore (last_accessed is null) + * @param projName + * @throws IOException + */ + void evict(String projName) throws IOException; + /* + * Called on a project when it must be restored. + * + * Pre: + * 1. projName should not be in repoStore + * 2. projName must be in swapStore + * 3. projName must be SWAPPED in dbStore (last_accessed is null) + * + * Acquires the project lock and restores projName. + * + * Post: + * 1. projName must be in repoStore + * 2. projName should not in swapStore + * 3. projName should be PRESENT in dbStore (last_accessed is not null) + * @param projName + * @throws IOException + */ + void restore(String projName) throws IOException; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobConfig.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobConfig.java index c517a23761..369bf5c12f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobConfig.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobConfig.java @@ -1,62 +1,63 @@ package uk.ac.ic.wlgitbridge.bridge.swap.job; -import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.bridge.swap.job.SwapJob.CompressionMethod; +import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by winston on 23/08/2016. */ public class SwapJobConfig { - private final int minProjects; - private final int lowGiB; - private final int highGiB; - private final long intervalMillis; - private final String compressionMethod; - private final boolean allowUnsafeStores; + private final int minProjects; + private final int lowGiB; + private final int highGiB; + private final long intervalMillis; + private final String compressionMethod; + private final boolean allowUnsafeStores; - public SwapJobConfig( - int minProjects, - int lowGiB, - int highGiB, - long intervalMillis, - String compressionMethod, - boolean allowUnsafeStores - ) { - this.minProjects = minProjects; - this.lowGiB = lowGiB; - this.highGiB = highGiB; - this.intervalMillis = intervalMillis; - this.compressionMethod = compressionMethod; - this.allowUnsafeStores = allowUnsafeStores; - } + public SwapJobConfig( + int minProjects, + int lowGiB, + int highGiB, + long intervalMillis, + String compressionMethod, + boolean allowUnsafeStores) { + this.minProjects = minProjects; + this.lowGiB = lowGiB; + this.highGiB = highGiB; + this.intervalMillis = intervalMillis; + this.compressionMethod = compressionMethod; + this.allowUnsafeStores = allowUnsafeStores; + } - public int getMinProjects() { - return minProjects; - } + public int getMinProjects() { + return minProjects; + } - public int getLowGiB() { - return lowGiB; - } + public int getLowGiB() { + return lowGiB; + } - public int getHighGiB() { - return highGiB; - } + public int getHighGiB() { + return highGiB; + } - public long getIntervalMillis() { - return intervalMillis; - } + public long getIntervalMillis() { + return intervalMillis; + } - public boolean getAllowUnsafeStores() { - return allowUnsafeStores; - } + public boolean getAllowUnsafeStores() { + return allowUnsafeStores; + } - public SwapJob.CompressionMethod getCompressionMethod() { - CompressionMethod result = SwapJob.stringToCompressionMethod(compressionMethod); - if (result == null) { - Log.info("SwapJobConfig: un-supported compressionMethod '{}', default to 'bzip2'", compressionMethod); - result = CompressionMethod.Bzip2; - } - return result; + public SwapJob.CompressionMethod getCompressionMethod() { + CompressionMethod result = SwapJob.stringToCompressionMethod(compressionMethod); + if (result == null) { + Log.info( + "SwapJobConfig: un-supported compressionMethod '{}', default to 'bzip2'", + compressionMethod); + result = CompressionMethod.Bzip2; } + return result; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImpl.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImpl.java index 6b42cac521..7101f8a8b5 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImpl.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImpl.java @@ -1,6 +1,14 @@ package uk.ac.ic.wlgitbridge.bridge.swap.job; import com.google.api.client.repackaged.com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Timer; +import java.util.concurrent.atomic.AtomicInteger; import uk.ac.ic.wlgitbridge.bridge.db.DBStore; import uk.ac.ic.wlgitbridge.bridge.lock.LockGuard; import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; @@ -10,259 +18,228 @@ import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.TimerUtils; -import java.io.IOException; -import java.io.InputStream; -import java.sql.Timestamp; -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Timer; -import java.util.concurrent.atomic.AtomicInteger; - -/** +/* * Created by winston on 20/08/2016. */ public class SwapJobImpl implements SwapJob { - private static final long GiB = (1l << 30); + private static final long GiB = (1l << 30); - int minProjects; - long lowWatermarkBytes; - long highWatermarkBytes; - Duration interval; + int minProjects; + long lowWatermarkBytes; + long highWatermarkBytes; + Duration interval; - private final ProjectLock lock; - private final RepoStore repoStore; - private final DBStore dbStore; - private final SwapStore swapStore; - private final CompressionMethod compressionMethod; + private final ProjectLock lock; + private final RepoStore repoStore; + private final DBStore dbStore; + private final SwapStore swapStore; + private final CompressionMethod compressionMethod; - private final Timer timer; + private final Timer timer; - final AtomicInteger swaps; + final AtomicInteger swaps; - public SwapJobImpl( - SwapJobConfig cfg, - ProjectLock lock, - RepoStore repoStore, - DBStore dbStore, - SwapStore swapStore - ) { - this( - cfg.getMinProjects(), - GiB * cfg.getLowGiB(), - GiB * cfg.getHighGiB(), - Duration.ofMillis(cfg.getIntervalMillis()), - cfg.getCompressionMethod(), - lock, - repoStore, - dbStore, - swapStore - ); + public SwapJobImpl( + SwapJobConfig cfg, + ProjectLock lock, + RepoStore repoStore, + DBStore dbStore, + SwapStore swapStore) { + this( + cfg.getMinProjects(), + GiB * cfg.getLowGiB(), + GiB * cfg.getHighGiB(), + Duration.ofMillis(cfg.getIntervalMillis()), + cfg.getCompressionMethod(), + lock, + repoStore, + dbStore, + swapStore); + } + + SwapJobImpl( + int minProjects, + long lowWatermarkBytes, + long highWatermarkBytes, + Duration interval, + CompressionMethod method, + ProjectLock lock, + RepoStore repoStore, + DBStore dbStore, + SwapStore swapStore) { + this.minProjects = minProjects; + this.lowWatermarkBytes = lowWatermarkBytes; + this.highWatermarkBytes = highWatermarkBytes; + this.interval = interval; + this.compressionMethod = method; + this.lock = lock; + this.repoStore = repoStore; + this.dbStore = dbStore; + this.swapStore = swapStore; + timer = new Timer(); + swaps = new AtomicInteger(0); + } + + @Override + public void start() { + timer.schedule(TimerUtils.makeTimerTask(this::doSwap), 0); + } + + @Override + public void stop() { + timer.cancel(); + } + + private void doSwap() { + try { + doSwap_(); + } catch (Throwable t) { + Log.warn("Exception thrown during swap job", t); } + timer.schedule(TimerUtils.makeTimerTask(this::doSwap), interval.toMillis()); + } - SwapJobImpl( - int minProjects, - long lowWatermarkBytes, - long highWatermarkBytes, - Duration interval, - CompressionMethod method, - ProjectLock lock, - RepoStore repoStore, - DBStore dbStore, - SwapStore swapStore - ) { - this.minProjects = minProjects; - this.lowWatermarkBytes = lowWatermarkBytes; - this.highWatermarkBytes = highWatermarkBytes; - this.interval = interval; - this.compressionMethod = method; - this.lock = lock; - this.repoStore = repoStore; - this.dbStore = dbStore; - this.swapStore = swapStore; - timer = new Timer(); - swaps = new AtomicInteger(0); + private void doSwap_() { + ArrayList exceptionProjectNames = new ArrayList(); + + Log.debug("Running swap number {}", swaps.get() + 1); + long totalSize = repoStore.totalSize(); + Log.debug("Size is {}/{} (high)", totalSize, highWatermarkBytes); + if (totalSize < highWatermarkBytes) { + Log.debug("No need to swap."); + swaps.incrementAndGet(); + return; } - - @Override - public void start() { - timer.schedule( - TimerUtils.makeTimerTask(this::doSwap), - 0 - ); - } - - @Override - public void stop() { - timer.cancel(); - } - - private void doSwap() { - try { - doSwap_(); - } catch (Throwable t) { - Log.warn("Exception thrown during swap job", t); + int numProjects = dbStore.getNumProjects(); + // while we have too many projects on disk + while ((totalSize = repoStore.totalSize()) > lowWatermarkBytes + && (numProjects = dbStore.getNumUnswappedProjects()) > minProjects) { + // check if we've had too many exceptions so far + if (exceptionProjectNames.size() >= 20) { + StringBuilder sb = new StringBuilder(); + for (String s : exceptionProjectNames) { + sb.append(s); + sb.append(' '); } - timer.schedule( - TimerUtils.makeTimerTask(this::doSwap), - interval.toMillis() - ); + Log.error( + "Too many exceptions while running swap, giving up on this run: {}", sb.toString()); + break; + } + // get the oldest project and try to swap it + String projectName = dbStore.getOldestUnswappedProject(); + try { + evict(projectName); + } catch (Exception e) { + Log.warn("[{}] Exception while swapping, mark project and move on", projectName, e); + // NOTE: this is something of a hack. If a project fails to swap we get stuck in a + // loop where `dbStore.getOldestUnswappedProject()` gives the same failing project over and + // over again, + // which fills up the disk with errors. By touching the access time we can mark the project + // as a + // non-candidate for swapping. Ideally we should be checking the logs for these log events + // and fixing + // whatever is wrong with the project + dbStore.setLastAccessedTime(projectName, Timestamp.valueOf(LocalDateTime.now())); + exceptionProjectNames.add(projectName); + } } - - private void doSwap_() { - ArrayList exceptionProjectNames = new ArrayList(); - - Log.debug("Running swap number {}", swaps.get() + 1); - long totalSize = repoStore.totalSize(); - Log.debug("Size is {}/{} (high)", totalSize, highWatermarkBytes); - if (totalSize < highWatermarkBytes) { - Log.debug("No need to swap."); - swaps.incrementAndGet(); - return; - } - int numProjects = dbStore.getNumProjects(); - // while we have too many projects on disk - while ( - (totalSize = repoStore.totalSize()) > lowWatermarkBytes && - (numProjects = dbStore.getNumUnswappedProjects()) > minProjects - ) { - // check if we've had too many exceptions so far - if (exceptionProjectNames.size() >= 20) { - StringBuilder sb = new StringBuilder(); - for (String s: exceptionProjectNames) { - sb.append(s); - sb.append(' '); - } - Log.error( - "Too many exceptions while running swap, giving up on this run: {}", - sb.toString() - ); - break; - } - // get the oldest project and try to swap it - String projectName = dbStore.getOldestUnswappedProject(); - try { - evict(projectName); - } catch (Exception e) { - Log.warn("[{}] Exception while swapping, mark project and move on", projectName, e); - // NOTE: this is something of a hack. If a project fails to swap we get stuck in a - // loop where `dbStore.getOldestUnswappedProject()` gives the same failing project over and over again, - // which fills up the disk with errors. By touching the access time we can mark the project as a - // non-candidate for swapping. Ideally we should be checking the logs for these log events and fixing - // whatever is wrong with the project - dbStore.setLastAccessedTime( - projectName, - Timestamp.valueOf(LocalDateTime.now()) - ); - exceptionProjectNames.add(projectName); - } - } - if (totalSize > lowWatermarkBytes) { - Log.warn( - "Finished swapping, but total size is still too high." - ); - } - Log.debug( - "Size: {}/{} (low), " + - "{} (high), " + - "projects on disk: {}/{}, " + - "min projects on disk: {}", - totalSize, - lowWatermarkBytes, - highWatermarkBytes, - numProjects, - dbStore.getNumProjects(), - minProjects - ); - swaps.incrementAndGet(); + if (totalSize > lowWatermarkBytes) { + Log.warn("Finished swapping, but total size is still too high."); } + Log.debug( + "Size: {}/{} (low), " + + "{} (high), " + + "projects on disk: {}/{}, " + + "min projects on disk: {}", + totalSize, + lowWatermarkBytes, + highWatermarkBytes, + numProjects, + dbStore.getNumProjects(), + minProjects); + swaps.incrementAndGet(); + } - /** - * @see SwapJob#evict(String) for high-level description. - * - * 1. Acquires the project lock. - * 2. Gets a bz2 stream and size of a project from the repo store, or throws - * 3. Uploads the bz2 stream and size to the projName in the swapStore. - * 4. Sets the last accessed time in the dbStore to null, which makes our - * state SWAPPED - * 5. Removes the project from the repo store. - * @param projName - * @throws IOException - */ - @Override - public void evict(String projName) throws IOException { - Preconditions.checkNotNull(projName, "projName was null"); - Log.info("Evicting project: {}", projName); - try (LockGuard __ = lock.lockGuard(projName)) { - try { - repoStore.gcProject(projName); - } catch (Exception e) { - Log.error("[{}] Exception while running gc on project: {}", projName, e); - } - long[] sizePtr = new long[1]; - try (InputStream blob = getBlobStream(projName, sizePtr)) { - swapStore.upload(projName, blob, sizePtr[0]); - String compression = SwapJob.compressionMethodAsString(compressionMethod); - if (compression == null) { - throw new RuntimeException("invalid compression method, should not happen"); - } - dbStore.swap(projName, compression); - repoStore.remove(projName); - } - } catch (CannotAcquireLockException e) { - Log.warn("[{}] Cannot acquire project lock, skipping swap", projName); - return; - } - Log.info("Evicted project: {}", projName); - } - - private InputStream getBlobStream(String projName, long[] sizePtr) throws IOException { - if (compressionMethod == CompressionMethod.Gzip) { - return repoStore.gzipProject(projName, sizePtr); - } else if (compressionMethod == CompressionMethod.Bzip2) { - return repoStore.bzip2Project(projName, sizePtr); - } else { + /* + * @see SwapJob#evict(String) for high-level description. + * + * 1. Acquires the project lock. + * 2. Gets a bz2 stream and size of a project from the repo store, or throws + * 3. Uploads the bz2 stream and size to the projName in the swapStore. + * 4. Sets the last accessed time in the dbStore to null, which makes our + * state SWAPPED + * 5. Removes the project from the repo store. + * @param projName + * @throws IOException + */ + @Override + public void evict(String projName) throws IOException { + Preconditions.checkNotNull(projName, "projName was null"); + Log.info("Evicting project: {}", projName); + try (LockGuard __ = lock.lockGuard(projName)) { + try { + repoStore.gcProject(projName); + } catch (Exception e) { + Log.error("[{}] Exception while running gc on project: {}", projName, e); + } + long[] sizePtr = new long[1]; + try (InputStream blob = getBlobStream(projName, sizePtr)) { + swapStore.upload(projName, blob, sizePtr[0]); + String compression = SwapJob.compressionMethodAsString(compressionMethod); + if (compression == null) { throw new RuntimeException("invalid compression method, should not happen"); } + dbStore.swap(projName, compression); + repoStore.remove(projName); + } + } catch (CannotAcquireLockException e) { + Log.warn("[{}] Cannot acquire project lock, skipping swap", projName); + return; } + Log.info("Evicted project: {}", projName); + } - /** - * @see SwapJob#restore(String) for high-level description. - * - * 1. Acquires the project lock. - * 2. Gets a bz2 stream for the project from the swapStore. - * 3. Fully downloads and places the bz2 stream back in the repo store. - * 4. Sets the last accessed time in the dbStore to now, which makes our - * state PRESENT and the last project to be evicted. - * @param projName - * @throws IOException - */ - @Override - public void restore(String projName) throws IOException { - try (LockGuard __ = lock.lockGuard(projName)) { - try (InputStream zipped = swapStore.openDownloadStream(projName)) { - String compression = dbStore.getSwapCompression(projName); - if (compression == null) { - throw new RuntimeException("Missing compression method during restore, should not happen"); - } - if ("gzip".equals(compression)) { - repoStore.ungzipProject( - projName, - zipped - ); - } else if ("bzip2".equals(compression)) { - repoStore.unbzip2Project( - projName, - zipped - ); - } - swapStore.remove(projName); - dbStore.restore(projName); - } - } catch (CannotAcquireLockException e) { - throw new RuntimeException(e); + private InputStream getBlobStream(String projName, long[] sizePtr) throws IOException { + if (compressionMethod == CompressionMethod.Gzip) { + return repoStore.gzipProject(projName, sizePtr); + } else if (compressionMethod == CompressionMethod.Bzip2) { + return repoStore.bzip2Project(projName, sizePtr); + } else { + throw new RuntimeException("invalid compression method, should not happen"); + } + } + + /* + * @see SwapJob#restore(String) for high-level description. + * + * 1. Acquires the project lock. + * 2. Gets a bz2 stream for the project from the swapStore. + * 3. Fully downloads and places the bz2 stream back in the repo store. + * 4. Sets the last accessed time in the dbStore to now, which makes our + * state PRESENT and the last project to be evicted. + * @param projName + * @throws IOException + */ + @Override + public void restore(String projName) throws IOException { + try (LockGuard __ = lock.lockGuard(projName)) { + try (InputStream zipped = swapStore.openDownloadStream(projName)) { + String compression = dbStore.getSwapCompression(projName); + if (compression == null) { + throw new RuntimeException( + "Missing compression method during restore, should not happen"); } + if ("gzip".equals(compression)) { + repoStore.ungzipProject(projName, zipped); + } else if ("bzip2".equals(compression)) { + repoStore.unbzip2Project(projName, zipped); + } + swapStore.remove(projName); + dbStore.restore(projName); + } + } catch (CannotAcquireLockException e) { + throw new RuntimeException(e); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStore.java index e3940840a5..3c7ae0164b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStore.java @@ -1,58 +1,49 @@ package uk.ac.ic.wlgitbridge.bridge.swap.store; -import org.apache.commons.io.IOUtils; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import org.apache.commons.io.IOUtils; -/** +/* * Created by winston on 23/08/2016. */ public class InMemorySwapStore implements SwapStore { - private final Map store; + private final Map store; - public InMemorySwapStore() { - store = new HashMap<>(); - } + public InMemorySwapStore() { + store = new HashMap<>(); + } - public InMemorySwapStore(SwapStoreConfig __) { - this(); - } + public InMemorySwapStore(SwapStoreConfig __) { + this(); + } - @Override - public void upload( - String projectName, - InputStream uploadStream, - long contentLength - ) throws IOException { - store.put( - projectName, - IOUtils.toByteArray(uploadStream, contentLength) - ); - } + @Override + public void upload(String projectName, InputStream uploadStream, long contentLength) + throws IOException { + store.put(projectName, IOUtils.toByteArray(uploadStream, contentLength)); + } - @Override - public InputStream openDownloadStream(String projectName) { - byte[] buf = store.get(projectName); - if (buf == null) { - throw new IllegalArgumentException( - "no such project in swap store: " + projectName - ); - } - return new ByteArrayInputStream(buf); + @Override + public InputStream openDownloadStream(String projectName) { + byte[] buf = store.get(projectName); + if (buf == null) { + throw new IllegalArgumentException("no such project in swap store: " + projectName); } + return new ByteArrayInputStream(buf); + } - @Override - public void remove(String projectName) { - store.remove(projectName); - } + @Override + public void remove(String projectName) { + store.remove(projectName); + } - @Override - public boolean isSafe() { - return false; - } + @Override + public boolean isSafe() { + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/NoopSwapStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/NoopSwapStore.java index a03c0ce704..b607b483b7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/NoopSwapStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/NoopSwapStore.java @@ -3,30 +3,26 @@ package uk.ac.ic.wlgitbridge.bridge.swap.store; import java.io.ByteArrayInputStream; import java.io.InputStream; -/** +/* * Created by winston on 24/08/2016. */ public class NoopSwapStore implements SwapStore { - public NoopSwapStore(SwapStoreConfig __) {} + public NoopSwapStore(SwapStoreConfig __) {} - @Override - public void upload( - String projectName, - InputStream uploadStream, - long contentLength - ) {} + @Override + public void upload(String projectName, InputStream uploadStream, long contentLength) {} - @Override - public InputStream openDownloadStream(String projectName) { - return new ByteArrayInputStream(new byte[0]); - } + @Override + public InputStream openDownloadStream(String projectName) { + return new ByteArrayInputStream(new byte[0]); + } - @Override - public void remove(String projectName) {} + @Override + public void remove(String projectName) {} - @Override - public boolean isSafe() { - return false; - } + @Override + public boolean isSafe() { + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStore.java index 4c0820d0cc..34e87dbe13 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStore.java @@ -5,86 +5,60 @@ import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.*; - import java.io.InputStream; -/** +/* * Created by winston on 21/08/2016. */ public class S3SwapStore implements SwapStore { - private final AmazonS3 s3; + private final AmazonS3 s3; - private final String bucketName; + private final String bucketName; - public S3SwapStore(SwapStoreConfig cfg) { - this( - cfg.getAwsAccessKey(), - cfg.getAwsSecret(), - cfg.getS3BucketName(), - cfg.getAwsRegion() - ); + public S3SwapStore(SwapStoreConfig cfg) { + this(cfg.getAwsAccessKey(), cfg.getAwsSecret(), cfg.getS3BucketName(), cfg.getAwsRegion()); + } + + S3SwapStore(String accessKey, String secret, String bucketName, String region) { + String regionToUse = null; + if (region == null) { + regionToUse = "us-east-1"; + } else { + regionToUse = region; } + s3 = + AmazonS3ClientBuilder.standard() + .withRegion(regionToUse) + .withCredentials( + new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secret))) + .build(); + this.bucketName = bucketName; + } - S3SwapStore( - String accessKey, - String secret, - String bucketName, - String region - ) { - String regionToUse = null; - if (region == null) { - regionToUse = "us-east-1"; - } else { - regionToUse = region; - } - s3 = AmazonS3ClientBuilder - .standard() - .withRegion(regionToUse) - .withCredentials( - new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secret)) - ).build(); - this.bucketName = bucketName; - } + @Override + public void upload(String projectName, InputStream uploadStream, long contentLength) { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(contentLength); + PutObjectRequest put = new PutObjectRequest(bucketName, projectName, uploadStream, metadata); + PutObjectResult res = s3.putObject(put); + } - @Override - public void upload( - String projectName, - InputStream uploadStream, - long contentLength - ) { - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentLength(contentLength); - PutObjectRequest put = new PutObjectRequest( - bucketName, - projectName, - uploadStream, - metadata - ); - PutObjectResult res = s3.putObject(put); - } + @Override + public InputStream openDownloadStream(String projectName) { + GetObjectRequest get = new GetObjectRequest(bucketName, projectName); + S3Object res = s3.getObject(get); + return res.getObjectContent(); + } - @Override - public InputStream openDownloadStream(String projectName) { - GetObjectRequest get = new GetObjectRequest( - bucketName, - projectName - ); - S3Object res = s3.getObject(get); - return res.getObjectContent(); - } + @Override + public void remove(String projectName) { + DeleteObjectRequest del = new DeleteObjectRequest(bucketName, projectName); + s3.deleteObject(del); + } - @Override - public void remove(String projectName) { - DeleteObjectRequest del = new DeleteObjectRequest( - bucketName, - projectName - ); - s3.deleteObject(del); - } - - @Override - public boolean isSafe() { - return true; - } + @Override + public boolean isSafe() { + return true; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStore.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStore.java index aa847f1780..3cf7cf26bf 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStore.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStore.java @@ -7,45 +7,37 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -/** +/* * Created by winston on 20/08/2016. */ public interface SwapStore { - Map> swapStores = - new HashMap>() { + Map> swapStores = + new HashMap>() { - { - put("noop", NoopSwapStore::new); - put("memory", InMemorySwapStore::new); - put("s3", S3SwapStore::new); - } + { + put("noop", NoopSwapStore::new); + put("memory", InMemorySwapStore::new); + put("s3", S3SwapStore::new); + } + }; - }; + static SwapStore fromConfig(Optional cfg) { + SwapStoreConfig cfg_ = cfg.orElse(SwapStoreConfig.NOOP); + String type = cfg_.getType(); + return swapStores.get(type).apply(cfg_); + } - static SwapStore fromConfig( - Optional cfg - ) { - SwapStoreConfig cfg_ = cfg.orElse(SwapStoreConfig.NOOP); - String type = cfg_.getType(); - return swapStores.get(type).apply(cfg_); - } + void upload(String projectName, InputStream uploadStream, long contentLength) throws IOException; - void upload( - String projectName, - InputStream uploadStream, - long contentLength - ) throws IOException; + InputStream openDownloadStream(String projectName); - InputStream openDownloadStream(String projectName); - - void remove(String projectName); - - /** - * Returns true if the swap store safely persists swapped projects. - * - * Fake swap stores should return false. - */ - boolean isSafe(); + void remove(String projectName); + /* + * Returns true if the swap store safely persists swapped projects. + * + * Fake swap stores should return false. + */ + boolean isSafe(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStoreConfig.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStoreConfig.java index 7559b240ec..d0cc806423 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStoreConfig.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/swap/store/SwapStoreConfig.java @@ -1,85 +1,64 @@ package uk.ac.ic.wlgitbridge.bridge.swap.store; -/** +/* * Created by winston on 24/08/2016. */ public class SwapStoreConfig { - public static final SwapStoreConfig NOOP = new SwapStoreConfig( - "noop", - null, - null, - null, - null - ); + public static final SwapStoreConfig NOOP = new SwapStoreConfig("noop", null, null, null, null); - private String type; - private String awsAccessKey; - private String awsSecret; - private String s3BucketName; - private String awsRegion; + private String type; + private String awsAccessKey; + private String awsSecret; + private String s3BucketName; + private String awsRegion; - public SwapStoreConfig() {} + public SwapStoreConfig() {} - public SwapStoreConfig( - String awsAccessKey, - String awsSecret, - String s3BucketName, - String awsRegion - ) { - this( - "s3", - awsAccessKey, - awsSecret, - s3BucketName, - awsRegion - ); - } + public SwapStoreConfig( + String awsAccessKey, String awsSecret, String s3BucketName, String awsRegion) { + this("s3", awsAccessKey, awsSecret, s3BucketName, awsRegion); + } - SwapStoreConfig( - String type, - String awsAccessKey, - String awsSecret, - String s3BucketName, - String awsRegion - ) { - this.type = type; - this.awsAccessKey = awsAccessKey; - this.awsSecret = awsSecret; - this.s3BucketName = s3BucketName; - this.awsRegion = awsRegion; - } + SwapStoreConfig( + String type, String awsAccessKey, String awsSecret, String s3BucketName, String awsRegion) { + this.type = type; + this.awsAccessKey = awsAccessKey; + this.awsSecret = awsSecret; + this.s3BucketName = s3BucketName; + this.awsRegion = awsRegion; + } - public String getType() { - return type; - } + public String getType() { + return type; + } - public String getAwsAccessKey() { - return awsAccessKey; - } + public String getAwsAccessKey() { + return awsAccessKey; + } - public String getAwsSecret() { - return awsSecret; - } + public String getAwsSecret() { + return awsSecret; + } - public String getS3BucketName() { - return s3BucketName; - } + public String getS3BucketName() { + return s3BucketName; + } - public String getAwsRegion() { return awsRegion; } + public String getAwsRegion() { + return awsRegion; + } - public SwapStoreConfig sanitisedCopy() { - return new SwapStoreConfig( - type, - awsAccessKey == null ? null : "", - awsSecret == null ? null : "", - s3BucketName, - awsRegion - ); - } + public SwapStoreConfig sanitisedCopy() { + return new SwapStoreConfig( + type, + awsAccessKey == null ? null : "", + awsSecret == null ? null : "", + s3BucketName, + awsRegion); + } - public static SwapStoreConfig sanitisedCopy(SwapStoreConfig swapStore) { - return swapStore == null ? null : swapStore.sanitisedCopy(); - } - + public static SwapStoreConfig sanitisedCopy(SwapStoreConfig swapStore) { + return swapStore == null ? null : swapStore.sanitisedCopy(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/util/CastUtil.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/util/CastUtil.java index 8dd4238551..079b4850df 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/util/CastUtil.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/util/CastUtil.java @@ -2,17 +2,15 @@ package uk.ac.ic.wlgitbridge.bridge.util; import com.google.common.base.Preconditions; -/** +/* * Created by winston on 01/07/2017. */ public class CastUtil { - public static int assumeInt(long l) { - Preconditions.checkArgument( - l <= (long) Integer.MAX_VALUE - && l >= (long) Integer.MIN_VALUE, - l + " cannot fit inside an int"); - return (int) l; - } - + public static int assumeInt(long l) { + Preconditions.checkArgument( + l <= (long) Integer.MAX_VALUE && l >= (long) Integer.MIN_VALUE, + l + " cannot fit inside an int"); + return (int) l; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CandidateSnapshot.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CandidateSnapshot.java index 4c3aef49f8..52689b8c7f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CandidateSnapshot.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CandidateSnapshot.java @@ -3,148 +3,128 @@ package uk.ac.ic.wlgitbridge.data; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import uk.ac.ic.wlgitbridge.data.filestore.RawFile; -import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; -import uk.ac.ic.wlgitbridge.util.Util; - import java.io.File; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; +import uk.ac.ic.wlgitbridge.data.filestore.RawFile; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 16/11/14. */ public class CandidateSnapshot implements AutoCloseable { - private final String projectName; - private final int currentVersion; - private final List files; - private final List deleted; - private File attsDirectory; + private final String projectName; + private final int currentVersion; + private final List files; + private final List deleted; + private File attsDirectory; - public CandidateSnapshot( - String projectName, - int currentVersion, - RawDirectory directoryContents, - RawDirectory oldDirectoryContents - ) { - this.projectName = projectName; - this.currentVersion = currentVersion; - files = diff(directoryContents, oldDirectoryContents); - deleted = deleted(directoryContents, oldDirectoryContents); + public CandidateSnapshot( + String projectName, + int currentVersion, + RawDirectory directoryContents, + RawDirectory oldDirectoryContents) { + this.projectName = projectName; + this.currentVersion = currentVersion; + files = diff(directoryContents, oldDirectoryContents); + deleted = deleted(directoryContents, oldDirectoryContents); + } + + private List diff( + RawDirectory directoryContents, RawDirectory oldDirectoryContents) { + List files = new LinkedList(); + Map fileTable = directoryContents.getFileTable(); + Map oldFileTable = oldDirectoryContents.getFileTable(); + for (Entry entry : fileTable.entrySet()) { + RawFile file = entry.getValue(); + files.add(new ServletFile(file, oldFileTable.get(file.getPath()))); } + return files; + } - private List diff( - RawDirectory directoryContents, - RawDirectory oldDirectoryContents - ) { - List files = new LinkedList(); - Map fileTable = directoryContents.getFileTable(); - Map oldFileTable = oldDirectoryContents.getFileTable(); - for (Entry entry : fileTable.entrySet()) { - RawFile file = entry.getValue(); - files.add(new ServletFile(file, oldFileTable.get(file.getPath()))); - } - return files; + private List deleted(RawDirectory directoryContents, RawDirectory oldDirectoryContents) { + List deleted = new LinkedList(); + Map fileTable = directoryContents.getFileTable(); + for (Entry entry : oldDirectoryContents.getFileTable().entrySet()) { + String path = entry.getKey(); + RawFile newFile = fileTable.get(path); + if (newFile == null) { + deleted.add(path); + } } + return deleted; + } - private List deleted( - RawDirectory directoryContents, - RawDirectory oldDirectoryContents - ) { - List deleted = new LinkedList(); - Map fileTable = directoryContents.getFileTable(); - for ( - Entry entry : - oldDirectoryContents.getFileTable().entrySet() - ) { - String path = entry.getKey(); - RawFile newFile = fileTable.get(path); - if (newFile == null) { - deleted.add(path); - } - } - return deleted; + public void writeServletFiles(File rootGitDirectory) throws IOException { + attsDirectory = new File(rootGitDirectory, ".wlgb/atts/" + projectName); + for (ServletFile file : files) { + if (file.isChanged()) { + file.writeToDiskWithName(attsDirectory, file.getUniqueIdentifier()); + } } + } - public void writeServletFiles(File rootGitDirectory) throws IOException { - attsDirectory = new File( - rootGitDirectory, - ".wlgb/atts/" + projectName - ); - for (ServletFile file : files) { - if (file.isChanged()) { - file.writeToDiskWithName(attsDirectory, file.getUniqueIdentifier()); - } - } + public void deleteServletFiles() throws IOException { + if (attsDirectory != null) { + Util.deleteDirectory(attsDirectory); } + } - public void deleteServletFiles() throws IOException { - if (attsDirectory != null) { - Util.deleteDirectory(attsDirectory); - } + public JsonElement getJsonRepresentation(String postbackKey) { + String projectURL = Util.getPostbackURL() + "api/" + projectName; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("latestVerId", currentVersion); + jsonObject.add("files", getFilesAsJson(projectURL, postbackKey)); + jsonObject.addProperty("postbackUrl", projectURL + "/" + postbackKey + "/postback"); + return jsonObject; + } + + private JsonArray getFilesAsJson(String projectURL, String postbackKey) { + JsonArray filesArray = new JsonArray(); + for (ServletFile file : files) { + filesArray.add(getFileAsJson(file, projectURL, postbackKey)); } + return filesArray; + } - public JsonElement getJsonRepresentation(String postbackKey) { - String projectURL = Util.getPostbackURL() + "api/" + projectName; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("latestVerId", currentVersion); - jsonObject.add("files", getFilesAsJson(projectURL, postbackKey)); - jsonObject.addProperty( - "postbackUrl", projectURL + "/" + postbackKey + "/postback" - ); - return jsonObject; + private JsonObject getFileAsJson(ServletFile file, String projectURL, String postbackKey) { + JsonObject jsonFile = new JsonObject(); + jsonFile.addProperty("name", file.getPath()); + if (file.isChanged()) { + String identifier = file.getUniqueIdentifier(); + String url = projectURL + "/" + identifier + "?key=" + postbackKey; + jsonFile.addProperty("url", url); } + return jsonFile; + } - private JsonArray getFilesAsJson(String projectURL, String postbackKey) { - JsonArray filesArray = new JsonArray(); - for (ServletFile file : files) { - filesArray.add(getFileAsJson(file, projectURL, postbackKey)); - } - return filesArray; - } + public String getProjectName() { + return projectName; + } - private JsonObject getFileAsJson( - ServletFile file, - String projectURL, - String postbackKey - ) { - JsonObject jsonFile = new JsonObject(); - jsonFile.addProperty("name", file.getPath()); - if (file.isChanged()) { - String identifier = file.getUniqueIdentifier(); - String url = projectURL + "/" + identifier + "?key=" + postbackKey; - jsonFile.addProperty("url", url); - } - return jsonFile; - } + public List getDeleted() { + return deleted; + } - public String getProjectName() { - return projectName; - } - - public List getDeleted() { - return deleted; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("VersionId: "); - sb.append(currentVersion); - sb.append(", files: "); - sb.append(files); - sb.append(", deleted: "); - sb.append(deleted); - return sb.toString(); - } - - @Override - public void close() throws IOException { - deleteServletFiles(); - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("VersionId: "); + sb.append(currentVersion); + sb.append(", files: "); + sb.append(files); + sb.append(", deleted: "); + sb.append(deleted); + return sb.toString(); + } + @Override + public void close() throws IOException { + deleteServletFiles(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CannotAcquireLockException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CannotAcquireLockException.java index 79206dd639..e28af65802 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CannotAcquireLockException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/CannotAcquireLockException.java @@ -1,9 +1,9 @@ package uk.ac.ic.wlgitbridge.data; public class CannotAcquireLockException extends Exception { - String projectName; + String projectName; - public CannotAcquireLockException() { - super("Another operation is in progress. Please try again later."); - } + public CannotAcquireLockException() { + super("Another operation is in progress. Please try again later."); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/LockAllWaiter.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/LockAllWaiter.java index a148aea70e..ae67da2822 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/LockAllWaiter.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/LockAllWaiter.java @@ -1,10 +1,9 @@ package uk.ac.ic.wlgitbridge.data; -/** +/* * Created by Winston on 21/02/15. */ public interface LockAllWaiter { - void threadsRemaining(int threads); - + void threadsRemaining(int threads); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ProjectLockImpl.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ProjectLockImpl.java index d9fa8018ca..0d5475d3ef 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ProjectLockImpl.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ProjectLockImpl.java @@ -1,98 +1,96 @@ package uk.ac.ic.wlgitbridge.data; -import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; - import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import uk.ac.ic.wlgitbridge.bridge.lock.ProjectLock; import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 20/11/14. */ public class ProjectLockImpl implements ProjectLock { - private final Map projectLocks; - private final ReentrantReadWriteLock rwlock; - private final Lock rlock; - private final ReentrantReadWriteLock.WriteLock wlock; - private LockAllWaiter waiter; - private boolean waiting; + private final Map projectLocks; + private final ReentrantReadWriteLock rwlock; + private final Lock rlock; + private final ReentrantReadWriteLock.WriteLock wlock; + private LockAllWaiter waiter; + private boolean waiting; - public ProjectLockImpl() { - projectLocks = new HashMap(); - rwlock = new ReentrantReadWriteLock(); - rlock = rwlock.readLock(); - wlock = rwlock.writeLock(); - waiting = false; + public ProjectLockImpl() { + projectLocks = new HashMap(); + rwlock = new ReentrantReadWriteLock(); + rlock = rwlock.readLock(); + wlock = rwlock.writeLock(); + waiting = false; + } + + public ProjectLockImpl(LockAllWaiter waiter) { + this(); + setWaiter(waiter); + } + + @Override + public void lockForProject(String projectName) throws CannotAcquireLockException { + Log.debug("[{}] taking project lock", projectName); + Lock projectLock = getLockForProjectName(projectName); + try { + if (!projectLock.tryLock(5, TimeUnit.SECONDS)) { + Log.debug("[{}] failed to acquire project lock", projectName); + throw new CannotAcquireLockException(); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); } + Log.debug("[{}] taking reentrant lock", projectName); + rlock.lock(); + Log.debug("[{}] taken locks", projectName); + } - public ProjectLockImpl(LockAllWaiter waiter) { - this(); - setWaiter(waiter); + @Override + public void unlockForProject(String projectName) { + Log.debug("[{}] releasing project lock", projectName); + getLockForProjectName(projectName).unlock(); + Log.debug("[{}] releasing reentrant lock", projectName); + rlock.unlock(); + Log.debug("[{}] released locks", projectName); + if (waiting) { + Log.debug("[{}] waiting for remaining threads", projectName); + trySignal(); } + } - @Override - public void lockForProject(String projectName) throws CannotAcquireLockException { - Log.debug("[{}] taking project lock", projectName); - Lock projectLock = getLockForProjectName(projectName); - try { - if (!projectLock.tryLock(5, TimeUnit.SECONDS)) { - Log.debug("[{}] failed to acquire project lock", projectName); - throw new CannotAcquireLockException(); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - Log.debug("[{}] taking reentrant lock", projectName); - rlock.lock(); - Log.debug("[{}] taken locks", projectName); + private void trySignal() { + int threads = rwlock.getReadLockCount(); + Log.debug("-> waiting for {} threads", threads); + if (waiter != null && threads > 0) { + waiter.threadsRemaining(threads); } + Log.debug("-> finished waiting for threads"); + } - @Override - public void unlockForProject(String projectName) { - Log.debug("[{}] releasing project lock", projectName); - getLockForProjectName(projectName).unlock(); - Log.debug("[{}] releasing reentrant lock", projectName); - rlock.unlock(); - Log.debug("[{}] released locks", projectName); - if (waiting) { - Log.debug("[{}] waiting for remaining threads", projectName); - trySignal(); - } - } - - private void trySignal() { - int threads = rwlock.getReadLockCount(); - Log.debug("-> waiting for {} threads", threads); - if (waiter != null && threads > 0) { - waiter.threadsRemaining(threads); - } - Log.debug("-> finished waiting for threads"); - } - - public void lockAll() { - Log.debug("-> locking all threads"); - waiting = true; - trySignal(); - Log.debug("-> locking reentrant write lock"); - wlock.lock(); - } - - private synchronized Lock getLockForProjectName(String projectName) { - Lock lock = projectLocks.get(projectName); - if (lock == null) { - lock = new ReentrantLock(); - projectLocks.put(projectName, lock); - } - return lock; - } - - public void setWaiter(LockAllWaiter waiter) { - this.waiter = waiter; + public void lockAll() { + Log.debug("-> locking all threads"); + waiting = true; + trySignal(); + Log.debug("-> locking reentrant write lock"); + wlock.lock(); + } + + private synchronized Lock getLockForProjectName(String projectName) { + Lock lock = projectLocks.get(projectName); + if (lock == null) { + lock = new ReentrantLock(); + projectLocks.put(projectName, lock); } + return lock; + } + public void setWaiter(LockAllWaiter waiter) { + this.waiter = waiter; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ServletFile.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ServletFile.java index cabd761a98..acf12f955b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ServletFile.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/ServletFile.java @@ -1,46 +1,48 @@ package uk.ac.ic.wlgitbridge.data; -import uk.ac.ic.wlgitbridge.data.filestore.RawFile; import java.util.UUID; +import uk.ac.ic.wlgitbridge.data.filestore.RawFile; -/** +/* * Created by Winston on 21/02/15. */ public class ServletFile extends RawFile { - private final RawFile file; - private final boolean changed; - private String uuid; + private final RawFile file; + private final boolean changed; + private String uuid; - public ServletFile(RawFile file, RawFile oldFile) { - this.file = file; - this.uuid = UUID.randomUUID().toString(); - changed = !equals(oldFile); - } + public ServletFile(RawFile file, RawFile oldFile) { + this.file = file; + this.uuid = UUID.randomUUID().toString(); + changed = !equals(oldFile); + } - public String getUniqueIdentifier() { return uuid; } + public String getUniqueIdentifier() { + return uuid; + } - @Override - public String getPath() { - return file.getPath(); - } + @Override + public String getPath() { + return file.getPath(); + } - @Override - public byte[] getContents() { - return file.getContents(); - } + @Override + public byte[] getContents() { + return file.getContents(); + } - @Override - public long size() { - return getContents().length; - } + @Override + public long size() { + return getContents().length; + } - public boolean isChanged() { - return changed; - } + public boolean isChanged() { + return changed; + } - @Override - public String toString() { - return getPath(); - } + @Override + public String toString() { + return getPath(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/GitDirectoryContents.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/GitDirectoryContents.java index 0186efedbc..cc53c98425 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/GitDirectoryContents.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/GitDirectoryContents.java @@ -1,84 +1,76 @@ package uk.ac.ic.wlgitbridge.data.filestore; -import uk.ac.ic.wlgitbridge.data.model.Snapshot; -import uk.ac.ic.wlgitbridge.util.Util; - import java.io.File; import java.io.IOException; import java.util.Date; import java.util.List; +import uk.ac.ic.wlgitbridge.data.model.Snapshot; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 14/11/14. */ public class GitDirectoryContents { - private final List files; - private final File gitDirectory; - private final String userName; - private final String userEmail; - private final String commitMessage; - private final Date when; + private final List files; + private final File gitDirectory; + private final String userName; + private final String userEmail; + private final String commitMessage; + private final Date when; - public GitDirectoryContents( - List files, - File rootGitDirectory, - String projectName, - String userName, - String userEmail, - String commitMessage, - Date when - ) { - this.files = files; - this.gitDirectory = new File(rootGitDirectory, projectName); - this.userName = userName; - this.userEmail = userEmail; - this.commitMessage = commitMessage; - this.when = when; + public GitDirectoryContents( + List files, + File rootGitDirectory, + String projectName, + String userName, + String userEmail, + String commitMessage, + Date when) { + this.files = files; + this.gitDirectory = new File(rootGitDirectory, projectName); + this.userName = userName; + this.userEmail = userEmail; + this.commitMessage = commitMessage; + this.when = when; + } + + public GitDirectoryContents( + List files, File rootGitDirectory, String projectName, Snapshot snapshot) { + this( + files, + rootGitDirectory, + projectName, + snapshot.getUserName(), + snapshot.getUserEmail(), + snapshot.getComment(), + snapshot.getCreatedAt()); + } + + public void write() throws IOException { + Util.deleteInDirectoryApartFrom(gitDirectory, ".git"); + for (RawFile fileNode : files) { + fileNode.writeToDisk(gitDirectory); } + } - public GitDirectoryContents( - List files, - File rootGitDirectory, - String projectName, - Snapshot snapshot - ) { - this( - files, - rootGitDirectory, - projectName, - snapshot.getUserName(), - snapshot.getUserEmail(), - snapshot.getComment(), - snapshot.getCreatedAt() - ); - } + public File getDirectory() { + return gitDirectory; + } - public void write() throws IOException { - Util.deleteInDirectoryApartFrom(gitDirectory, ".git"); - for (RawFile fileNode : files) { - fileNode.writeToDisk(gitDirectory); - } - } + public String getUserName() { + return userName; + } - public File getDirectory() { - return gitDirectory; - } + public String getUserEmail() { + return userEmail; + } - public String getUserName() { - return userName; - } - - public String getUserEmail() { - return userEmail; - } - - public String getCommitMessage() { - return commitMessage; - } - - public Date getWhen() { - return when; - } + public String getCommitMessage() { + return commitMessage; + } + public Date getWhen() { + return when; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawDirectory.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawDirectory.java index db8a554282..ccaf3f876d 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawDirectory.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawDirectory.java @@ -1,23 +1,19 @@ package uk.ac.ic.wlgitbridge.data.filestore; -import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException; - import java.util.Map; -import java.util.Optional; -/** +/* * Created by Winston on 16/11/14. */ public class RawDirectory { - private final Map fileTable; + private final Map fileTable; - public RawDirectory(Map fileTable) { - this.fileTable = fileTable; - } - - public Map getFileTable() { - return fileTable; - } + public RawDirectory(Map fileTable) { + this.fileTable = fileTable; + } + public Map getFileTable() { + return fileTable; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawFile.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawFile.java index bfd84bb78e..a0056ec154 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawFile.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RawFile.java @@ -1,46 +1,43 @@ package uk.ac.ic.wlgitbridge.data.filestore; -import uk.ac.ic.wlgitbridge.util.Log; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; +import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 16/11/14. */ public abstract class RawFile { - public abstract String getPath(); + public abstract String getPath(); - public abstract byte[] getContents(); + public abstract byte[] getContents(); - public abstract long size(); + public abstract long size(); - public final void writeToDisk(File directory) throws IOException { - writeToDiskWithName(directory, getPath()); + public final void writeToDisk(File directory) throws IOException { + writeToDiskWithName(directory, getPath()); + } + + public final void writeToDiskWithName(File directory, String name) throws IOException { + File file = new File(directory, name); + file.getParentFile().mkdirs(); + file.createNewFile(); + OutputStream out = new FileOutputStream(file); + out.write(getContents()); + out.close(); + Log.debug("Wrote file: {}", file.getAbsolutePath()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RawFile)) { + return false; } - - public final void writeToDiskWithName(File directory, String name) throws IOException { - File file = new File(directory, name); - file.getParentFile().mkdirs(); - file.createNewFile(); - OutputStream out = new FileOutputStream(file); - out.write(getContents()); - out.close(); - Log.debug("Wrote file: {}", file.getAbsolutePath()); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof RawFile)) { - return false; - } - RawFile that = (RawFile) obj; - return getPath().equals(that.getPath()) - && Arrays.equals(getContents(), that.getContents()); - } - + RawFile that = (RawFile) obj; + return getPath().equals(that.getPath()) && Arrays.equals(getContents(), that.getContents()); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RepositoryFile.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RepositoryFile.java index e7cc331aec..5bb6ab3731 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RepositoryFile.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/filestore/RepositoryFile.java @@ -1,31 +1,30 @@ package uk.ac.ic.wlgitbridge.data.filestore; -/** +/* * Created by Winston on 16/11/14. */ public class RepositoryFile extends RawFile { - private final String path; - private final byte[] contents; + private final String path; + private final byte[] contents; - public RepositoryFile(String path, byte[] contents) { - this.path = path; - this.contents = contents; - } + public RepositoryFile(String path, byte[] contents) { + this.path = path; + this.contents = contents; + } - @Override - public String getPath() { - return path; - } + @Override + public String getPath() { + return path; + } - @Override - public byte[] getContents() { - return contents; - } - - @Override - public long size() { - return contents.length; - } + @Override + public byte[] getContents() { + return contents; + } + @Override + public long size() { + return contents.length; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/model/Snapshot.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/model/Snapshot.java index 467691997c..bfd2e80895 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/model/Snapshot.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/model/Snapshot.java @@ -1,5 +1,7 @@ package uk.ac.ic.wlgitbridge.data.model; +import java.util.Date; +import java.util.List; import org.joda.time.DateTime; import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotAttachment; import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotData; @@ -7,71 +9,67 @@ import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotFile; import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.SnapshotInfo; import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.WLUser; -import java.util.Date; -import java.util.List; - -/** +/* * Created by Winston on 03/11/14. */ public class Snapshot implements Comparable { - private final int versionID; - private final String comment; - private final String userName; - private final String userEmail; - private final Date createdAt; + private final int versionID; + private final String comment; + private final String userName; + private final String userEmail; + private final Date createdAt; - private final List srcs; - private final List atts; + private final List srcs; + private final List atts; - public Snapshot(SnapshotInfo info, SnapshotData data) { - versionID = info.getVersionId(); - comment = info.getComment(); - WLUser user = info.getUser(); - userName = user.getName(); - userEmail = user.getEmail(); - createdAt = new DateTime(info.getCreatedAt()).toDate(); + public Snapshot(SnapshotInfo info, SnapshotData data) { + versionID = info.getVersionId(); + comment = info.getComment(); + WLUser user = info.getUser(); + userName = user.getName(); + userEmail = user.getEmail(); + createdAt = new DateTime(info.getCreatedAt()).toDate(); - srcs = data.getSrcs(); - atts = data.getAtts(); - } + srcs = data.getSrcs(); + atts = data.getAtts(); + } - public int getVersionID() { - return versionID; - } + public int getVersionID() { + return versionID; + } - public String getComment() { - return comment; - } + public String getComment() { + return comment; + } - public String getUserName() { - return userName; - } + public String getUserName() { + return userName; + } - public String getUserEmail() { - return userEmail; - } + public String getUserEmail() { + return userEmail; + } - public List getSrcs() { - return srcs; - } + public List getSrcs() { + return srcs; + } - public List getAtts() { - return atts; - } + public List getAtts() { + return atts; + } - public Date getCreatedAt() { - return createdAt; - } + public Date getCreatedAt() { + return createdAt; + } - @Override - public int compareTo(Snapshot snapshot) { - return Integer.compare(versionID, snapshot.versionID); - } - - @Override - public String toString() { - return String.valueOf(versionID); - } + @Override + public int compareTo(Snapshot snapshot) { + return Integer.compare(versionID, snapshot.versionID); + } + @Override + public String toString() { + return String.valueOf(versionID); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/FileLimitExceededException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/FileLimitExceededException.java index f25767a4db..0a4b39cded 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/FileLimitExceededException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/FileLimitExceededException.java @@ -1,34 +1,31 @@ package uk.ac.ic.wlgitbridge.git.exception; -import uk.ac.ic.wlgitbridge.util.Util; - import java.util.Arrays; import java.util.List; -import java.util.Optional; public class FileLimitExceededException extends GitUserException { - private final long numFiles; + private final long numFiles; - private final long maxFiles; + private final long maxFiles; - public FileLimitExceededException(long numFiles, long maxFiles) { - this.numFiles = numFiles; - this.maxFiles = maxFiles; - } + public FileLimitExceededException(long numFiles, long maxFiles) { + this.numFiles = numFiles; + this.maxFiles = maxFiles; + } - @Override - public String getMessage() { - return "too many files"; - } - - @Override - public List getDescriptionLines() { - return Arrays.asList( - "repository contains " + - numFiles + " files, which exceeds the limit of " + - maxFiles + " files" - ); - } + @Override + public String getMessage() { + return "too many files"; + } + @Override + public List getDescriptionLines() { + return Arrays.asList( + "repository contains " + + numFiles + + " files, which exceeds the limit of " + + maxFiles + + " files"); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/GitUserException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/GitUserException.java index 91a7478c89..ed8716e044 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/GitUserException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/GitUserException.java @@ -2,13 +2,12 @@ package uk.ac.ic.wlgitbridge.git.exception; import java.util.List; -/** +/* * Created by winston on 20/08/2016. */ public abstract class GitUserException extends Exception { - public abstract String getMessage(); - - public abstract List getDescriptionLines(); + public abstract String getMessage(); + public abstract List getDescriptionLines(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/InvalidGitRepository.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/InvalidGitRepository.java index de4129c44c..404189dc57 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/InvalidGitRepository.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/InvalidGitRepository.java @@ -5,18 +5,16 @@ import java.util.List; public class InvalidGitRepository extends GitUserException { - @Override - public String getMessage() { - return "invalid git repo"; - } - - @Override - public List getDescriptionLines() { - return Arrays.asList( - "Your Git repository contains a reference we cannot resolve.", - "If your project contains a Git submodule,", - "please remove it and try again." - ); - } + @Override + public String getMessage() { + return "invalid git repo"; + } + @Override + public List getDescriptionLines() { + return Arrays.asList( + "Your Git repository contains a reference we cannot resolve.", + "If your project contains a Git submodule,", + "please remove it and try again."); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SizeLimitExceededException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SizeLimitExceededException.java index 69c6bd014a..c6d0787401 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SizeLimitExceededException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SizeLimitExceededException.java @@ -1,40 +1,34 @@ package uk.ac.ic.wlgitbridge.git.exception; -import uk.ac.ic.wlgitbridge.util.Util; - import java.util.Arrays; import java.util.List; import java.util.Optional; +import uk.ac.ic.wlgitbridge.util.Util; public class SizeLimitExceededException extends GitUserException { - private final Optional path; + private final Optional path; - private final long actualSize; + private final long actualSize; - private final long maxSize; + private final long maxSize; - public SizeLimitExceededException( - Optional path, long actualSize, long maxSize) { - this.path = path; - this.actualSize = actualSize; - this.maxSize = maxSize; - } + public SizeLimitExceededException(Optional path, long actualSize, long maxSize) { + this.path = path; + this.actualSize = actualSize; + this.maxSize = maxSize; + } - @Override - public String getMessage() { - return "file too big"; - } - - @Override - public List getDescriptionLines() { - String filename = - path.isPresent() ? "File '" + path.get() + "' is" : "There's a file"; - return Arrays.asList( - filename + " too large to push to " - + Util.getServiceName() + " via git", - "the recommended maximum file size is 50 MiB" - ); - } + @Override + public String getMessage() { + return "file too big"; + } + @Override + public List getDescriptionLines() { + String filename = path.isPresent() ? "File '" + path.get() + "' is" : "There's a file"; + return Arrays.asList( + filename + " too large to push to " + Util.getServiceName() + " via git", + "the recommended maximum file size is 50 MiB"); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SnapshotAPIException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SnapshotAPIException.java index 0a5e5a0529..733e1565d9 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SnapshotAPIException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/exception/SnapshotAPIException.java @@ -2,9 +2,7 @@ package uk.ac.ic.wlgitbridge.git.exception; import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; -/** +/* * Created by winston on 20/08/2016. */ -public abstract class SnapshotAPIException - extends GitUserException - implements JSONSource {} +public abstract class SnapshotAPIException extends GitUserException implements JSONSource {} diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLReceivePackFactory.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLReceivePackFactory.java index ada1e656a5..b8392cf1d7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLReceivePackFactory.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLReceivePackFactory.java @@ -1,81 +1,68 @@ package uk.ac.ic.wlgitbridge.git.handler; import com.google.api.client.auth.oauth2.Credential; +import java.util.Optional; +import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import uk.ac.ic.wlgitbridge.bridge.Bridge; import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; -import uk.ac.ic.wlgitbridge.bridge.snapshot.SnapshotApi; import uk.ac.ic.wlgitbridge.git.handler.hook.WriteLatexPutHook; -import uk.ac.ic.wlgitbridge.git.servlet.WLGitServlet; import uk.ac.ic.wlgitbridge.server.Oauth2Filter; import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.Util; -import javax.servlet.http.HttpServletRequest; -import java.util.Optional; - -/** +/* * Created by Winston on 02/11/14. */ -/** +/* * One of the "big three" interfaces created by {@link WLGitServlet} to handle * user Git requests. * * This class just puts a {@link WriteLatexPutHook} into the {@link ReceivePack} * that it returns. */ -public class WLReceivePackFactory - implements ReceivePackFactory { +public class WLReceivePackFactory implements ReceivePackFactory { - private final RepoStore repoStore; + private final RepoStore repoStore; - private final Bridge bridge; + private final Bridge bridge; - public WLReceivePackFactory(RepoStore repoStore, Bridge bridge) { - this.repoStore = repoStore; - this.bridge = bridge; + public WLReceivePackFactory(RepoStore repoStore, Bridge bridge) { + this.repoStore = repoStore; + this.bridge = bridge; + } + + /* + * Puts a {@link WriteLatexPutHook} into the returned {@link ReceivePack}. + * + * The {@link WriteLatexPutHook} needs our hostname, which we get from the + * original {@link HttpServletRequest}, used to provide a postback URL to + * the {@link SnapshotApi}. We also give it the oauth2 that we injected in + * the {@link Oauth2Filter}, and the {@link Bridge}. + * + * At this point, the repository will have been synced to the latest on + * Overleaf, but it's possible that an update happens on Overleaf while our + * put hook is running. In this case, we fail, and the user tries again, + * triggering another sync, and so on. + * @param httpServletRequest the original request + * @param repository the JGit {@link Repository} provided by + * {@link WLRepositoryResolver} + * @return a correctly hooked {@link ReceivePack} + */ + @Override + public ReceivePack create(HttpServletRequest httpServletRequest, Repository repository) { + Log.debug("[{}] Creating receive-pack", repository.getWorkTree().getName()); + Optional oauth2 = + Optional.ofNullable( + (Credential) httpServletRequest.getAttribute(Oauth2Filter.ATTRIBUTE_KEY)); + ReceivePack receivePack = new ReceivePack(repository); + String hostname = Util.getPostbackURL(); + if (hostname == null) { + hostname = httpServletRequest.getLocalName(); } - - /** - * Puts a {@link WriteLatexPutHook} into the returned {@link ReceivePack}. - * - * The {@link WriteLatexPutHook} needs our hostname, which we get from the - * original {@link HttpServletRequest}, used to provide a postback URL to - * the {@link SnapshotApi}. We also give it the oauth2 that we injected in - * the {@link Oauth2Filter}, and the {@link Bridge}. - * - * At this point, the repository will have been synced to the latest on - * Overleaf, but it's possible that an update happens on Overleaf while our - * put hook is running. In this case, we fail, and the user tries again, - * triggering another sync, and so on. - * @param httpServletRequest the original request - * @param repository the JGit {@link Repository} provided by - * {@link WLRepositoryResolver} - * @return a correctly hooked {@link ReceivePack} - */ - @Override - public ReceivePack create( - HttpServletRequest httpServletRequest, - Repository repository - ) { - Log.debug( - "[{}] Creating receive-pack", - repository.getWorkTree().getName() - ); - Optional oauth2 = Optional.ofNullable( - (Credential) httpServletRequest.getAttribute( - Oauth2Filter.ATTRIBUTE_KEY)); - ReceivePack receivePack = new ReceivePack(repository); - String hostname = Util.getPostbackURL(); - if (hostname == null) { - hostname = httpServletRequest.getLocalName(); - } - receivePack.setPreReceiveHook( - new WriteLatexPutHook(repoStore, bridge, hostname, oauth2) - ); - return receivePack; - } - + receivePack.setPreReceiveHook(new WriteLatexPutHook(repoStore, bridge, hostname, oauth2)); + return receivePack; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java index fdf80806a4..d9ddbda26b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java @@ -1,6 +1,9 @@ package uk.ac.ic.wlgitbridge.git.handler; import com.google.api.client.auth.oauth2.Credential; +import java.io.IOException; +import java.util.Optional; +import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.ServiceMayNotContinueException; @@ -9,23 +12,15 @@ import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import uk.ac.ic.wlgitbridge.bridge.Bridge; import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; -import uk.ac.ic.wlgitbridge.git.handler.hook.WriteLatexPutHook; -import uk.ac.ic.wlgitbridge.git.servlet.WLGitServlet; -import uk.ac.ic.wlgitbridge.server.GitBridgeServer; import uk.ac.ic.wlgitbridge.server.Oauth2Filter; import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException; import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.Util; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.Arrays; -import java.util.Optional; - -/** +/* * Created by Winston on 02/11/14. */ -/** +/* * One of the "big three" interfaces created by {@link WLGitServlet} to handle * user Git requests. * @@ -37,93 +32,81 @@ import java.util.Optional; * bringing it onto disk and applying commits to it until it is up-to-date with * Overleaf. */ -public class WLRepositoryResolver - implements RepositoryResolver { +public class WLRepositoryResolver implements RepositoryResolver { - private final Bridge bridge; + private final Bridge bridge; - public WLRepositoryResolver(Bridge bridge) { - this.bridge = bridge; + public WLRepositoryResolver(Bridge bridge) { + this.bridge = bridge; + } + + /* + * Calls into the Bridge to resolve a project name to a JGit + * {@link Repository}, or throw an exception. + * + * On success, the repository will have been brought onto disk and updated + * to the latest (synced). + * + * In the case of clones and fetches, upload packs are created from the + * returned JGit {@link Repository} by the {@link WLUploadPackFactory}. + * + * The project lock is acquired for this process so it can't be swapped out. + * + * However, it can still be swapped out between this and a Git push. The + * push would fail due to the project changed on Overleaf between the sync + * and the actual push to Overleaf (performed by the + * {@link WLReceivePackFactory} and {@link WriteLatexPutHook}. In this case, + * the user will have to try again (which prompts another update, etc. until + * this no longer happens). + * @param httpServletRequest The HttpServletRequest as required by the + * interface. We injected the oauth2 creds into it with + * {@link Oauth2Filter}, which was set up by the {@link GitBridgeServer}. + * @param name The name of the project + * @return the JGit {@link Repository}. + * @throws RepositoryNotFoundException If the project does not exist + * @throws ServiceNotAuthorizedException If the user did not auth when + * required to + * @throws ServiceMayNotContinueException If any other general user + * exception occurs that must be propogated back to the user, e.g. + * internal errors (IOException, etc), too large file, and so on. + */ + @Override + public Repository open(HttpServletRequest httpServletRequest, String name) + throws RepositoryNotFoundException, + ServiceNotAuthorizedException, + ServiceMayNotContinueException { + Log.debug("[{}] Request to open git repo", name); + Optional oauth2 = + Optional.ofNullable( + (Credential) httpServletRequest.getAttribute(Oauth2Filter.ATTRIBUTE_KEY)); + String projName = Util.removeAllSuffixes(name, "/", ".git"); + try { + return bridge.getUpdatedRepo(oauth2, projName).getJGitRepository(); + } catch (RepositoryNotFoundException e) { + Log.warn("Repository not found: " + name); + throw e; + /* + } catch (ServiceNotAuthorizedException e) { + cannot occur + } catch (ServiceNotEnabledException e) { + cannot occur + */ + } catch (ServiceMayNotContinueException e) { + /* Such as FailedConnectionException */ + throw e; + } catch (CannotAcquireLockException e) { + throw new ServiceMayNotContinueException(e.getMessage()); + } catch (RuntimeException e) { + Log.warn("Runtime exception when trying to open repo: " + projName, e); + throw new ServiceMayNotContinueException(e); + } catch (ForbiddenException e) { + throw new ServiceNotAuthorizedException(); + } catch (GitUserException e) { + throw new ServiceMayNotContinueException( + e.getMessage() + "\n" + String.join("\n", e.getDescriptionLines()), e); + } catch (IOException e) { + Log.warn("IOException when trying to open repo: " + projName, e); + throw new ServiceMayNotContinueException("Internal server error."); } - - /** - * Calls into the Bridge to resolve a project name to a JGit - * {@link Repository}, or throw an exception. - * - * On success, the repository will have been brought onto disk and updated - * to the latest (synced). - * - * In the case of clones and fetches, upload packs are created from the - * returned JGit {@link Repository} by the {@link WLUploadPackFactory}. - * - * The project lock is acquired for this process so it can't be swapped out. - * - * However, it can still be swapped out between this and a Git push. The - * push would fail due to the project changed on Overleaf between the sync - * and the actual push to Overleaf (performed by the - * {@link WLReceivePackFactory} and {@link WriteLatexPutHook}. In this case, - * the user will have to try again (which prompts another update, etc. until - * this no longer happens). - * @param httpServletRequest The HttpServletRequest as required by the - * interface. We injected the oauth2 creds into it with - * {@link Oauth2Filter}, which was set up by the {@link GitBridgeServer}. - * @param name The name of the project - * @return the JGit {@link Repository}. - * @throws RepositoryNotFoundException If the project does not exist - * @throws ServiceNotAuthorizedException If the user did not auth when - * required to - * @throws ServiceMayNotContinueException If any other general user - * exception occurs that must be propogated back to the user, e.g. - * internal errors (IOException, etc), too large file, and so on. - */ - @Override - public Repository open( - HttpServletRequest httpServletRequest, - String name - ) throws RepositoryNotFoundException, - ServiceNotAuthorizedException, - ServiceMayNotContinueException { - Log.debug("[{}] Request to open git repo", name); - Optional oauth2 = Optional.ofNullable( - (Credential) httpServletRequest.getAttribute( - Oauth2Filter.ATTRIBUTE_KEY)); - String projName = Util.removeAllSuffixes(name, "/", ".git"); - try { - return bridge.getUpdatedRepo(oauth2, projName).getJGitRepository(); - } catch (RepositoryNotFoundException e) { - Log.warn("Repository not found: " + name); - throw e; - /* - } catch (ServiceNotAuthorizedException e) { - cannot occur - } catch (ServiceNotEnabledException e) { - cannot occur - */ - } catch (ServiceMayNotContinueException e) { - /* Such as FailedConnectionException */ - throw e; - } catch (CannotAcquireLockException e) { - throw new ServiceMayNotContinueException(e.getMessage()); - } catch (RuntimeException e) { - Log.warn( - "Runtime exception when trying to open repo: " + projName, - e - ); - throw new ServiceMayNotContinueException(e); - } catch (ForbiddenException e) { - throw new ServiceNotAuthorizedException(); - } catch (GitUserException e) { - throw new ServiceMayNotContinueException( - e.getMessage() + "\n" + - String.join("\n", e.getDescriptionLines()), - e); - } catch (IOException e) { - Log.warn( - "IOException when trying to open repo: " + projName, - e - ); - throw new ServiceMayNotContinueException("Internal server error."); - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLUploadPackFactory.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLUploadPackFactory.java index 945e95a1aa..646d08cd12 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLUploadPackFactory.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLUploadPackFactory.java @@ -1,44 +1,34 @@ package uk.ac.ic.wlgitbridge.git.handler; +import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.resolver.UploadPackFactory; -import uk.ac.ic.wlgitbridge.git.servlet.WLGitServlet; import uk.ac.ic.wlgitbridge.util.Log; -import javax.servlet.http.HttpServletRequest; - -/** +/* * Created by Winston on 02/11/14. */ -/** +/* * One of the "big three" interfaces created by {@link WLGitServlet} to handle * user Git requests. * * The actual class doesn't do much, and most of the work is done when the * project name is being resolved by the {@link WLRepositoryResolver}. */ -public class WLUploadPackFactory - implements UploadPackFactory { - - /** - * This does nothing special. Synchronising the project with Overleaf will - * have been performed by {@link WLRepositoryResolver}. - * @param __ Not used, required by the {@link UploadPackFactory} interface - * @param repository The JGit repository provided by the - * {@link WLRepositoryResolver} - * @return the {@link UploadPack}, used by JGit to serve the request - */ - @Override - public UploadPack create( - HttpServletRequest __, - Repository repository - ) { - Log.debug( - "[{}] Creating upload-pack", - repository.getWorkTree().getName() - ); - return new UploadPack(repository); - } +public class WLUploadPackFactory implements UploadPackFactory { + /* + * This does nothing special. Synchronising the project with Overleaf will + * have been performed by {@link WLRepositoryResolver}. + * @param __ Not used, required by the {@link UploadPackFactory} interface + * @param repository The JGit repository provided by the + * {@link WLRepositoryResolver} + * @return the {@link UploadPack}, used by JGit to serve the request + */ + @Override + public UploadPack create(HttpServletRequest __, Repository repository) { + Log.debug("[{}] Creating upload-pack", repository.getWorkTree().getName()); + return new UploadPack(repository); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/WriteLatexPutHook.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/WriteLatexPutHook.java index 9d2a783fc5..24979f8ec3 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/WriteLatexPutHook.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/WriteLatexPutHook.java @@ -1,6 +1,10 @@ package uk.ac.ic.wlgitbridge.git.handler.hook; import com.google.api.client.auth.oauth2.Credential; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PreReceiveHook; import org.eclipse.jgit.transport.ReceiveCommand; @@ -11,176 +15,129 @@ import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; -import uk.ac.ic.wlgitbridge.git.handler.WLReceivePackFactory; import uk.ac.ic.wlgitbridge.git.handler.hook.exception.ForcedPushException; import uk.ac.ic.wlgitbridge.git.handler.hook.exception.WrongBranchException; import uk.ac.ic.wlgitbridge.snapshot.push.exception.InternalErrorException; import uk.ac.ic.wlgitbridge.snapshot.push.exception.OutOfDateException; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; import uk.ac.ic.wlgitbridge.util.Log; -import java.io.IOException; -import java.util.Collection; -import java.util.Iterator; -import java.util.Optional; - -/** +/* * Created by Winston on 03/11/14. */ -/** +/* * Created by {@link WLReceivePackFactory} to update the {@link Bridge} for a * user's Git push request, or fail with an error. The hook is able to approve * or reject a request. */ public class WriteLatexPutHook implements PreReceiveHook { - private final RepoStore repoStore; + private final RepoStore repoStore; - private final Bridge bridge; - private final String hostname; - private final Optional oauth2; + private final Bridge bridge; + private final String hostname; + private final Optional oauth2; - /** - * The constructor to use, which provides the hook with the {@link Bridge}, - * the hostname (used to construct a URL to give to Overleaf to postback), - * and the oauth2 (used to authenticate with the Snapshot API). - * @param repoStore - * @param bridge the {@link Bridge} - * @param hostname the hostname used for postback from the Snapshot API - * @param oauth2 used to authenticate with the snapshot API, or null - */ - public WriteLatexPutHook( - RepoStore repoStore, - Bridge bridge, - String hostname, - Optional oauth2 - ) { - this.repoStore = repoStore; - this.bridge = bridge; - this.hostname = hostname; - this.oauth2 = oauth2; + /* + * The constructor to use, which provides the hook with the {@link Bridge}, + * the hostname (used to construct a URL to give to Overleaf to postback), + * and the oauth2 (used to authenticate with the Snapshot API). + * @param repoStore + * @param bridge the {@link Bridge} + * @param hostname the hostname used for postback from the Snapshot API + * @param oauth2 used to authenticate with the snapshot API, or null + */ + public WriteLatexPutHook( + RepoStore repoStore, Bridge bridge, String hostname, Optional oauth2) { + this.repoStore = repoStore; + this.bridge = bridge; + this.hostname = hostname; + this.oauth2 = oauth2; + } + + @Override + public void onPreReceive(ReceivePack receivePack, Collection receiveCommands) { + Log.debug( + "-> Handling {} commands in {}", + receiveCommands.size(), + receivePack.getRepository().getDirectory().getAbsolutePath()); + for (ReceiveCommand receiveCommand : receiveCommands) { + try { + handleReceiveCommand(oauth2, receivePack.getRepository(), receiveCommand); + } catch (IOException e) { + Log.error("IOException on pre receive", e); + receivePack.sendError(e.getMessage()); + receiveCommand.setResult(Result.REJECTED_OTHER_REASON, e.getMessage()); + } catch (OutOfDateException e) { + Log.error("OutOfDateException on pre receive", e); + receiveCommand.setResult(Result.REJECTED_NONFASTFORWARD); + } catch (GitUserException e) { + Log.error("GitUserException on pre receive", e); + handleSnapshotPostException(receivePack, receiveCommand, e); + } catch (CannotAcquireLockException e) { + Log.info("CannotAcquireLockException on pre receive"); + receivePack.sendError(e.getMessage()); + receiveCommand.setResult(Result.REJECTED_OTHER_REASON, e.getMessage()); + } catch (Throwable t) { + Log.error("Throwable on pre receive", t); + handleSnapshotPostException(receivePack, receiveCommand, new InternalErrorException()); + } } + Log.debug( + "-> Handled {} commands in {}", + receiveCommands.size(), + receivePack.getRepository().getDirectory().getAbsolutePath()); + } - @Override - public void onPreReceive( - ReceivePack receivePack, - Collection receiveCommands - ) { - Log.debug("-> Handling {} commands in {}", receiveCommands.size(), receivePack.getRepository().getDirectory().getAbsolutePath()); - for (ReceiveCommand receiveCommand : receiveCommands) { - try { - handleReceiveCommand( - oauth2, - receivePack.getRepository(), - receiveCommand - ); - } catch (IOException e) { - Log.error("IOException on pre receive", e); - receivePack.sendError(e.getMessage()); - receiveCommand.setResult( - Result.REJECTED_OTHER_REASON, - e.getMessage() - ); - } catch (OutOfDateException e) { - Log.error("OutOfDateException on pre receive", e); - receiveCommand.setResult(Result.REJECTED_NONFASTFORWARD); - } catch (GitUserException e) { - Log.error("GitUserException on pre receive", e); - handleSnapshotPostException(receivePack, receiveCommand, e); - } catch (CannotAcquireLockException e) { - Log.info("CannotAcquireLockException on pre receive"); - receivePack.sendError(e.getMessage()); - receiveCommand.setResult( - Result.REJECTED_OTHER_REASON, - e.getMessage() - ); - } catch (Throwable t) { - Log.error("Throwable on pre receive", t); - handleSnapshotPostException( - receivePack, - receiveCommand, - new InternalErrorException() - ); - } - } - Log.debug("-> Handled {} commands in {}", receiveCommands.size(), receivePack.getRepository().getDirectory().getAbsolutePath()); + private void handleSnapshotPostException( + ReceivePack receivePack, ReceiveCommand receiveCommand, GitUserException e) { + String message = e.getMessage(); + receivePack.sendError(message); + StringBuilder msg = new StringBuilder(); + for (Iterator it = e.getDescriptionLines().iterator(); it.hasNext(); ) { + String line = it.next(); + msg.append("hint: "); + msg.append(line); + if (it.hasNext()) { + msg.append('\n'); + } } + receivePack.sendMessage(""); + receivePack.sendMessage(msg.toString()); + receiveCommand.setResult(Result.REJECTED_OTHER_REASON, message); + } - private void handleSnapshotPostException( - ReceivePack receivePack, - ReceiveCommand receiveCommand, - GitUserException e - ) { - String message = e.getMessage(); - receivePack.sendError(message); - StringBuilder msg = new StringBuilder(); - for ( - Iterator it = e.getDescriptionLines().iterator(); - it.hasNext(); - ) { - String line = it.next(); - msg.append("hint: "); - msg.append(line); - if (it.hasNext()) { - msg.append('\n'); - } - } - receivePack.sendMessage(""); - receivePack.sendMessage(msg.toString()); - receiveCommand.setResult(Result.REJECTED_OTHER_REASON, message); + private void handleReceiveCommand( + Optional oauth2, Repository repository, ReceiveCommand receiveCommand) + throws IOException, GitUserException, CannotAcquireLockException { + checkBranch(receiveCommand); + checkForcedPush(receiveCommand); + bridge.push( + oauth2, + repository.getWorkTree().getName(), + getPushedDirectoryContents(repository, receiveCommand), + getOldDirectoryContents(repository), + hostname); + } + + private void checkBranch(ReceiveCommand receiveCommand) throws WrongBranchException { + if (!receiveCommand.getRefName().equals("refs/heads/master")) { + throw new WrongBranchException(); } + } - private void handleReceiveCommand( - Optional oauth2, - Repository repository, - ReceiveCommand receiveCommand - ) throws IOException, GitUserException, CannotAcquireLockException { - checkBranch(receiveCommand); - checkForcedPush(receiveCommand); - bridge.push( - oauth2, - repository.getWorkTree().getName(), - getPushedDirectoryContents(repository, - receiveCommand), - getOldDirectoryContents(repository), - hostname - ); + private void checkForcedPush(ReceiveCommand receiveCommand) throws ForcedPushException { + if (receiveCommand.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD) { + throw new ForcedPushException(); } + } - private void checkBranch( - ReceiveCommand receiveCommand - ) throws WrongBranchException { - if (!receiveCommand.getRefName().equals("refs/heads/master")) { - throw new WrongBranchException(); - } - } - - private void checkForcedPush( - ReceiveCommand receiveCommand - ) throws ForcedPushException { - if ( - receiveCommand.getType() - == ReceiveCommand.Type.UPDATE_NONFASTFORWARD - ) { - throw new ForcedPushException(); - } - } - - private RawDirectory getPushedDirectoryContents( - Repository repository, - ReceiveCommand receiveCommand - ) throws IOException, GitUserException { - return repoStore - .useJGitRepo(repository, receiveCommand.getNewId()) - .getDirectory(); - } - - private RawDirectory getOldDirectoryContents( - Repository repository - ) throws IOException, GitUserException { - return repoStore - .useJGitRepo(repository, repository.resolve("HEAD")) - .getDirectory(); - } + private RawDirectory getPushedDirectoryContents( + Repository repository, ReceiveCommand receiveCommand) throws IOException, GitUserException { + return repoStore.useJGitRepo(repository, receiveCommand.getNewId()).getDirectory(); + } + private RawDirectory getOldDirectoryContents(Repository repository) + throws IOException, GitUserException { + return repoStore.useJGitRepo(repository, repository.resolve("HEAD")).getDirectory(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/ForcedPushException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/ForcedPushException.java index 834964019f..d1d350944b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/ForcedPushException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/ForcedPushException.java @@ -1,37 +1,33 @@ package uk.ac.ic.wlgitbridge.git.handler.hook.exception; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.util.Util; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; - import java.util.Arrays; import java.util.List; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 16/11/14. */ public class ForcedPushException extends SnapshotPostException { - private static final String[] DESCRIPTION_LINES = { - "You can't git push --force to a " - + Util.getServiceName() - + " project.", - "Try to put your changes on top of the current head.", - "If everything else fails, delete and reclone your repository, " - + "make your changes, then push again." - }; + private static final String[] DESCRIPTION_LINES = { + "You can't git push --force to a " + Util.getServiceName() + " project.", + "Try to put your changes on top of the current head.", + "If everything else fails, delete and reclone your repository, " + + "make your changes, then push again." + }; - @Override - public String getMessage() { - return "forced push prohibited"; - } + @Override + public String getMessage() { + return "forced push prohibited"; + } - @Override - public List getDescriptionLines() { - return Arrays.asList(DESCRIPTION_LINES); - } - - @Override - public void fromJSON(JsonElement json) {} + @Override + public List getDescriptionLines() { + return Arrays.asList(DESCRIPTION_LINES); + } + @Override + public void fromJSON(JsonElement json) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/WrongBranchException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/WrongBranchException.java index a2d4f32e88..e216781f12 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/WrongBranchException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/hook/exception/WrongBranchException.java @@ -1,34 +1,29 @@ package uk.ac.ic.wlgitbridge.git.handler.hook.exception; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; - import java.util.Arrays; import java.util.List; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; -/** +/* * Created by Winston on 19/12/14. */ public class WrongBranchException extends SnapshotPostException { - private static final String[] DESCRIPTION_LINES = { - "You can't push any new branches.", - "Please use the master branch." - }; + private static final String[] DESCRIPTION_LINES = { + "You can't push any new branches.", "Please use the master branch." + }; - @Override - public String getMessage() { - return "wrong branch"; - } + @Override + public String getMessage() { + return "wrong branch"; + } - @Override - public List getDescriptionLines() { - return Arrays.asList(DESCRIPTION_LINES); - } - - @Override - public void fromJSON(JsonElement json) { - - } + @Override + public List getDescriptionLines() { + return Arrays.asList(DESCRIPTION_LINES); + } + @Override + public void fromJSON(JsonElement json) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServlet.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServlet.java index da491046ed..b29ad78cd6 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServlet.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServlet.java @@ -1,5 +1,6 @@ package uk.ac.ic.wlgitbridge.git.servlet; +import javax.servlet.ServletException; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jgit.http.server.GitServlet; import uk.ac.ic.wlgitbridge.bridge.Bridge; @@ -7,14 +8,11 @@ import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; import uk.ac.ic.wlgitbridge.git.handler.WLReceivePackFactory; import uk.ac.ic.wlgitbridge.git.handler.WLRepositoryResolver; import uk.ac.ic.wlgitbridge.git.handler.WLUploadPackFactory; -import uk.ac.ic.wlgitbridge.server.GitBridgeServer; -import javax.servlet.ServletException; - -/** +/* * Created by Winston on 02/11/14. */ -/** +/* * This is the Servlet created by the {@link GitBridgeServer} that does all of * the work in handling user Git requests and directing them to the * {@link Bridge}. @@ -28,25 +26,21 @@ import javax.servlet.ServletException; */ public class WLGitServlet extends GitServlet { - /** - * Constructor that sets all of the resolvers and factories for the - * {@link GitServlet}. - * - * Also needs to call init with a config ({@link WLGitServletConfig}, as - * required by the {@link GitServlet}. - * @param ctxHandler - * @param bridge - * @throws ServletException - */ - public WLGitServlet( - ServletContextHandler ctxHandler, - RepoStore repoStore, - Bridge bridge - ) throws ServletException { - setRepositoryResolver(new WLRepositoryResolver(bridge)); - setReceivePackFactory(new WLReceivePackFactory(repoStore, bridge)); - setUploadPackFactory(new WLUploadPackFactory()); - init(new WLGitServletConfig(ctxHandler)); - } - + /* + * Constructor that sets all of the resolvers and factories for the + * {@link GitServlet}. + * + * Also needs to call init with a config ({@link WLGitServletConfig}, as + * required by the {@link GitServlet}. + * @param ctxHandler + * @param bridge + * @throws ServletException + */ + public WLGitServlet(ServletContextHandler ctxHandler, RepoStore repoStore, Bridge bridge) + throws ServletException { + setRepositoryResolver(new WLRepositoryResolver(bridge)); + setReceivePackFactory(new WLReceivePackFactory(repoStore, bridge)); + setUploadPackFactory(new WLUploadPackFactory()); + init(new WLGitServletConfig(ctxHandler)); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServletConfig.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServletConfig.java index 64bcd69c98..022382acc7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServletConfig.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServletConfig.java @@ -1,42 +1,40 @@ package uk.ac.ic.wlgitbridge.git.servlet; -import org.eclipse.jetty.servlet.ServletContextHandler; - +import java.util.Enumeration; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; -import java.util.Enumeration; +import org.eclipse.jetty.servlet.ServletContextHandler; -/** +/* * Created by Winston on 02/11/14. */ public class WLGitServletConfig implements ServletConfig { - private static final String SERVLET_NAME = "git-servlet"; + private static final String SERVLET_NAME = "git-servlet"; - private ServletContext servletContext; + private ServletContext servletContext; - public WLGitServletConfig(ServletContextHandler ctxHandler) { - servletContext = ctxHandler.getServletContext(); - } + public WLGitServletConfig(ServletContextHandler ctxHandler) { + servletContext = ctxHandler.getServletContext(); + } - @Override - public String getServletName() { - return SERVLET_NAME; - } + @Override + public String getServletName() { + return SERVLET_NAME; + } - @Override - public ServletContext getServletContext() { - return servletContext; - } + @Override + public ServletContext getServletContext() { + return servletContext; + } - @Override - public String getInitParameter(String s) { - return null; - } - - @Override - public Enumeration getInitParameterNames() { - return null; - } + @Override + public String getInitParameter(String s) { + return null; + } + @Override + public Enumeration getInitParameterNames() { + return null; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/util/RepositoryObjectTreeWalker.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/util/RepositoryObjectTreeWalker.java index f7c9f555da..bdd966166f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/util/RepositoryObjectTreeWalker.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/util/RepositoryObjectTreeWalker.java @@ -1,5 +1,10 @@ package uk.ac.ic.wlgitbridge.git.util; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.Repository; @@ -12,91 +17,67 @@ import uk.ac.ic.wlgitbridge.data.filestore.RepositoryFile; import uk.ac.ic.wlgitbridge.git.exception.InvalidGitRepository; import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -/** +/* * Created by Winston on 16/11/14. */ public class RepositoryObjectTreeWalker { - private final TreeWalk treeWalk; - private final Repository repository; + private final TreeWalk treeWalk; + private final Repository repository; - public RepositoryObjectTreeWalker( - Repository repository, - ObjectId objectId - ) throws IOException { - treeWalk = initTreeWalk(repository, objectId); - this.repository = repository; + public RepositoryObjectTreeWalker(Repository repository, ObjectId objectId) throws IOException { + treeWalk = initTreeWalk(repository, objectId); + this.repository = repository; + } + + public RepositoryObjectTreeWalker(Repository repository) throws IOException { + this(repository, 0); + } + + public RepositoryObjectTreeWalker(Repository repository, int fromHead) throws IOException { + this(repository, repository.resolve("HEAD~" + fromHead)); + } + + public RawDirectory getDirectoryContents(Optional maxFileSize) + throws IOException, SizeLimitExceededException, InvalidGitRepository { + return new RawDirectory(walkGitObjectTree(maxFileSize)); + } + + private TreeWalk initTreeWalk(Repository repository, ObjectId objectId) throws IOException { + if (objectId == null) { + return null; } + RevWalk walk = new RevWalk(repository); + TreeWalk treeWalk = new TreeWalk(repository); + treeWalk.addTree(walk.parseCommit(objectId).getTree()); + treeWalk.setRecursive(true); + return treeWalk; + } - public RepositoryObjectTreeWalker( - Repository repository - ) throws IOException { - this(repository, 0); + private Map walkGitObjectTree(Optional maxFileSize) + throws IOException, SizeLimitExceededException, InvalidGitRepository { + Map fileContentsTable = new HashMap<>(); + if (treeWalk == null) { + return fileContentsTable; } + while (treeWalk.next()) { + String path = treeWalk.getPathString(); - public RepositoryObjectTreeWalker( - Repository repository, - int fromHead - ) throws IOException { - this(repository, repository.resolve("HEAD~" + fromHead)); + ObjectId objectId = treeWalk.getObjectId(0); + if (!repository.hasObject(objectId)) { + throw new InvalidGitRepository(); + } + ObjectLoader obj = repository.open(objectId); + long size = obj.getSize(); + if (maxFileSize.isPresent() && size > maxFileSize.get()) { + throw new SizeLimitExceededException(Optional.ofNullable(path), size, maxFileSize.get()); + } + try (ByteArrayOutputStream o = new ByteArrayOutputStream(CastUtil.assumeInt(size))) { + obj.copyTo(o); + fileContentsTable.put(path, new RepositoryFile(path, o.toByteArray())); + } + ; } - - public RawDirectory getDirectoryContents(Optional maxFileSize) - throws IOException, - SizeLimitExceededException, - InvalidGitRepository { - return new RawDirectory(walkGitObjectTree(maxFileSize)); - } - - private TreeWalk initTreeWalk( - Repository repository, - ObjectId objectId - ) throws IOException { - if (objectId == null) { - return null; - } - RevWalk walk = new RevWalk(repository); - TreeWalk treeWalk = new TreeWalk(repository); - treeWalk.addTree(walk.parseCommit(objectId).getTree()); - treeWalk.setRecursive(true); - return treeWalk; - } - - private Map walkGitObjectTree(Optional maxFileSize) - throws IOException, - SizeLimitExceededException, - InvalidGitRepository { - Map fileContentsTable = new HashMap<>(); - if (treeWalk == null) { - return fileContentsTable; - } - while (treeWalk.next()) { - String path = treeWalk.getPathString(); - - ObjectId objectId = treeWalk.getObjectId(0); - if (!repository.hasObject(objectId)) { - throw new InvalidGitRepository(); - } - ObjectLoader obj = repository.open(objectId); - long size = obj.getSize(); - if (maxFileSize.isPresent() && size > maxFileSize.get()) { - throw new SizeLimitExceededException( - Optional.ofNullable(path), size, maxFileSize.get()); - } - try (ByteArrayOutputStream o = new ByteArrayOutputStream( - CastUtil.assumeInt(size))) { - obj.copyTo(o); - fileContentsTable.put( - path, new RepositoryFile(path, o.toByteArray())); - }; - } - return fileContentsTable; - } - + return fileContentsTable; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClient.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClient.java index c22e2e7dbd..8d479ee079 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClient.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClient.java @@ -1,81 +1,67 @@ package uk.ac.ic.wlgitbridge.io.http.ning; import io.netty.handler.codec.http.HttpHeaders; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.concurrent.ExecutionException; import org.asynchttpclient.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.ic.wlgitbridge.util.FunctionT; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.concurrent.ExecutionException; - public class NingHttpClient implements NingHttpClientFacade { - private static final Logger log - = LoggerFactory.getLogger(NingHttpClient.class); + private static final Logger log = LoggerFactory.getLogger(NingHttpClient.class); - private final AsyncHttpClient http; + private final AsyncHttpClient http; - public NingHttpClient(AsyncHttpClient http) { - this.http = http; - } + public NingHttpClient(AsyncHttpClient http) { + this.http = http; + } - @Override - public byte[] get( - String url, - FunctionT handler - ) throws ExecutionException { - try { - return http - .prepareGet(url) - .execute(new AsyncCompletionHandler() { + @Override + public byte[] get(String url, FunctionT handler) + throws ExecutionException { + try { + return http.prepareGet(url) + .execute( + new AsyncCompletionHandler() { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); @Override - public State onHeadersReceived( - HttpHeaders headers - ) throws E { - return handler.apply(headers) - ? State.CONTINUE : State.ABORT; + public State onHeadersReceived(HttpHeaders headers) throws E { + return handler.apply(headers) ? State.CONTINUE : State.ABORT; } @Override - public State onBodyPartReceived( - HttpResponseBodyPart content - ) throws IOException { - bytes.write(content.getBodyPartBytes()); - return State.CONTINUE; + public State onBodyPartReceived(HttpResponseBodyPart content) throws IOException { + bytes.write(content.getBodyPartBytes()); + return State.CONTINUE; } @Override - public byte[] onCompleted( - Response response - ) throws Exception { - int statusCode = response.getStatusCode(); - if (statusCode >= 400) { - throw new Exception("got status " + statusCode + - " fetching " + url); - } - byte[] ret = bytes.toByteArray(); - bytes.close(); - log.debug( - statusCode - + " " - + response.getStatusText() - + " (" - + ret.length - + "B) -> " - + url - ); - return ret; + public byte[] onCompleted(Response response) throws Exception { + int statusCode = response.getStatusCode(); + if (statusCode >= 400) { + throw new Exception("got status " + statusCode + " fetching " + url); + } + byte[] ret = bytes.toByteArray(); + bytes.close(); + log.debug( + statusCode + + " " + + response.getStatusText() + + " (" + + ret.length + + "B) -> " + + url); + return ret; } - - }).get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + }) + .get(); + } catch (InterruptedException e) { + throw new RuntimeException(e); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClientFacade.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClientFacade.java index 2d1e0b3744..97616c0538 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClientFacade.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/io/http/ning/NingHttpClientFacade.java @@ -1,22 +1,18 @@ package uk.ac.ic.wlgitbridge.io.http.ning; import io.netty.handler.codec.http.HttpHeaders; -import uk.ac.ic.wlgitbridge.util.FunctionT; - import java.util.concurrent.ExecutionException; +import uk.ac.ic.wlgitbridge.util.FunctionT; public interface NingHttpClientFacade { - /** - * Performs a GET request - * @param url the target URL - * @param handler handler for the response headers. Returning false - * aborts the request. - * @return - */ - byte[] get( - String url, - FunctionT handler - ) throws ExecutionException; - + /* + * Performs a GET request + * @param url the target URL + * @param handler handler for the response headers. Returning false + * aborts the request. + * @return + */ + byte[] get(String url, FunctionT handler) + throws ExecutionException; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/BasicAuthCredentials.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/BasicAuthCredentials.java index 7bb3bc59d5..15827a8424 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/BasicAuthCredentials.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/BasicAuthCredentials.java @@ -17,5 +17,4 @@ public class BasicAuthCredentials { public String getPassword() { return password; } - } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/DiagnosticsHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/DiagnosticsHandler.java index 230e05ea81..064cae7bec 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/DiagnosticsHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/DiagnosticsHandler.java @@ -1,40 +1,26 @@ package uk.ac.ic.wlgitbridge.server; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.util.Log; - +import java.io.IOException; +import java.lang.management.ManagementFactory; import javax.management.JMException; import javax.management.ObjectName; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.Writer; -import java.lang.management.ManagementFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import uk.ac.ic.wlgitbridge.util.Log; public class DiagnosticsHandler extends AbstractHandler { - public DiagnosticsHandler() { - } + public DiagnosticsHandler() {} @Override public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException, ServletException { + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { String method = baseRequest.getMethod(); - if ( - ("GET".equals(method)) - && target != null - && target.matches("^/diags/?$") - ) { + if (("GET".equals(method)) && target != null && target.matches("^/diags/?$")) { baseRequest.setHandled(true); Log.debug(method + " <- /diags"); @@ -45,7 +31,7 @@ public class DiagnosticsHandler extends AbstractHandler { try { detail = execute("vmNativeMemory", "detail"); summary = execute("vmNativeMemory", "summary"); - } catch(JMException e) { + } catch (JMException e) { Log.error("Failed to get native memory detail: " + e.getMessage()); response.setStatus(500); return; @@ -62,10 +48,12 @@ public class DiagnosticsHandler extends AbstractHandler { } public static String execute(String command, String... args) throws JMException { - return (String) ManagementFactory.getPlatformMBeanServer().invoke( - new ObjectName("com.sun.management:type=DiagnosticCommand"), - command, - new Object[]{args}, - new String[]{"[Ljava.lang.String;"}); + return (String) + ManagementFactory.getPlatformMBeanServer() + .invoke( + new ObjectName("com.sun.management:type=DiagnosticCommand"), + command, + new Object[] {args}, + new String[] {"[Ljava.lang.String;"}); } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/FileHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/FileHandler.java index 4acae34e2a..20a3c94e0c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/FileHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/FileHandler.java @@ -1,5 +1,11 @@ package uk.ac.ic.wlgitbridge.server; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.ResourceHandler; import org.slf4j.Logger; @@ -7,58 +13,42 @@ import org.slf4j.LoggerFactory; import uk.ac.ic.wlgitbridge.bridge.Bridge; import uk.ac.ic.wlgitbridge.snapshot.push.exception.InvalidPostbackKeyException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** +/* * Serve files referenced by the snapshot that we send to the Overleaf API. * * Requests must include the postback key. */ public class FileHandler extends ResourceHandler { - private static final Logger LOG - = LoggerFactory.getLogger(FileHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(FileHandler.class); - private final Bridge bridge; - private final Pattern DOC_KEY_PATTERN = Pattern.compile("^/(\\w+)/.+$"); + private final Bridge bridge; + private final Pattern DOC_KEY_PATTERN = Pattern.compile("^/(\\w+)/.+$"); - public FileHandler(Bridge bridge) { - this.bridge = bridge; - } - - @Override - public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException, ServletException { - if (!"GET".equals(baseRequest.getMethod())) return; - LOG.debug("GET <- {}", baseRequest.getRequestURI()); - - Matcher docKeyMatcher = DOC_KEY_PATTERN.matcher(target); - if (!docKeyMatcher.matches()) return; - String docKey = docKeyMatcher.group(1); - - String apiKey = request.getParameter("key"); - if (apiKey == null) return; - - try { - bridge.checkPostbackKey(docKey, apiKey); - } catch (InvalidPostbackKeyException e) { - LOG.warn( - "INVALID POST BACK KEY: docKey={} apiKey={}", - docKey, - apiKey - ); - return; - } - - super.handle(target, baseRequest, request, response); + public FileHandler(Bridge bridge) { + this.bridge = bridge; + } + + @Override + public void handle( + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + if (!"GET".equals(baseRequest.getMethod())) return; + LOG.debug("GET <- {}", baseRequest.getRequestURI()); + + Matcher docKeyMatcher = DOC_KEY_PATTERN.matcher(target); + if (!docKeyMatcher.matches()) return; + String docKey = docKeyMatcher.group(1); + + String apiKey = request.getParameter("key"); + if (apiKey == null) return; + + try { + bridge.checkPostbackKey(docKey, apiKey); + } catch (InvalidPostbackKeyException e) { + LOG.warn("INVALID POST BACK KEY: docKey={} apiKey={}", docKey, apiKey); + return; } + super.handle(target, baseRequest, request, response); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitBridgeServer.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitBridgeServer.java index f0e700e2ee..30c5039212 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitBridgeServer.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitBridgeServer.java @@ -1,5 +1,12 @@ package uk.ac.ic.wlgitbridge.server; +import java.io.File; +import java.net.BindException; +import java.nio.file.Paths; +import java.util.EnumSet; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.ServletException; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -23,182 +30,143 @@ import uk.ac.ic.wlgitbridge.snapshot.base.SnapshotAPIRequest; import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.Util; -import javax.servlet.DispatcherType; -import javax.servlet.Filter; -import javax.servlet.ServletException; -import java.io.File; -import java.net.BindException; -import java.nio.file.Paths; -import java.util.EnumSet; - -/** +/* * Created by Winston on 02/11/14. */ -/** +/* * Class for the actual server. */ public class GitBridgeServer { - private final Bridge bridge; + private final Bridge bridge; - private final Server jettyServer; + private final Server jettyServer; - private final int port; - private String rootGitDirectoryPath; - private String apiBaseURL; + private final int port; + private String rootGitDirectoryPath; + private String apiBaseURL; - public GitBridgeServer( - Config config - ) throws ServletException { - org.eclipse.jetty.util.log.Log.setLog(new NullLogger()); - this.port = config.getPort(); - this.rootGitDirectoryPath = config.getRootGitDirectory(); - RepoStore repoStore = new FSGitRepoStore( - rootGitDirectoryPath, - config.getRepoStore().flatMap(RepoStoreConfig::getMaxFileSize) - ); - DBStore dbStore = new SqliteDBStore( - Paths.get( - repoStore.getRootDirectory().getAbsolutePath() - ).resolve(".wlgb").resolve("wlgb.db").toFile(), - config.getSqliteHeapLimitBytes() - ); - SwapStore swapStore = SwapStore.fromConfig(config.getSwapStore()); - SnapshotApi snapshotApi = new NetSnapshotApi(); - bridge = Bridge.make( - config, - repoStore, - dbStore, - swapStore, - snapshotApi - ); - jettyServer = new Server(); - configureJettyServer(config, repoStore, snapshotApi); - apiBaseURL = config.getAPIBaseURL(); - SnapshotAPIRequest.setBaseURL(apiBaseURL); - Util.setServiceName(config.getServiceName()); - Util.setPostbackURL(config.getPostbackURL()); - Util.setPort(config.getPort()); + public GitBridgeServer(Config config) throws ServletException { + org.eclipse.jetty.util.log.Log.setLog(new NullLogger()); + this.port = config.getPort(); + this.rootGitDirectoryPath = config.getRootGitDirectory(); + RepoStore repoStore = + new FSGitRepoStore( + rootGitDirectoryPath, config.getRepoStore().flatMap(RepoStoreConfig::getMaxFileSize)); + DBStore dbStore = + new SqliteDBStore( + Paths.get(repoStore.getRootDirectory().getAbsolutePath()) + .resolve(".wlgb") + .resolve("wlgb.db") + .toFile(), + config.getSqliteHeapLimitBytes()); + SwapStore swapStore = SwapStore.fromConfig(config.getSwapStore()); + SnapshotApi snapshotApi = new NetSnapshotApi(); + bridge = Bridge.make(config, repoStore, dbStore, swapStore, snapshotApi); + jettyServer = new Server(); + configureJettyServer(config, repoStore, snapshotApi); + apiBaseURL = config.getAPIBaseURL(); + SnapshotAPIRequest.setBaseURL(apiBaseURL); + Util.setServiceName(config.getServiceName()); + Util.setPostbackURL(config.getPostbackURL()); + Util.setPort(config.getPort()); + } + + /* + * Starts the server on the port given on construction. + */ + public void start() { + try { + bridge.checkDB(); + jettyServer.start(); + bridge.startBackgroundJobs(); + Log.info(Util.getServiceName() + "-Git Bridge server started"); + Log.info("Listening on port: " + port); + Log.info("Bridged to: " + apiBaseURL); + Log.info("Postback base URL: " + Util.getPostbackURL()); + Log.info("Root git directory path: " + rootGitDirectoryPath); + } catch (BindException e) { + Log.error("Failed to bind Jetty", e); + } catch (Exception e) { + Log.error("Failed to start Jetty", e); } + } - /** - * Starts the server on the port given on construction. - */ - public void start() { - try { - bridge.checkDB(); - jettyServer.start(); - bridge.startBackgroundJobs(); - Log.info(Util.getServiceName() + "-Git Bridge server started"); - Log.info("Listening on port: " + port); - Log.info("Bridged to: " + apiBaseURL); - Log.info("Postback base URL: " + Util.getPostbackURL()); - Log.info("Root git directory path: " + rootGitDirectoryPath); - } catch (BindException e) { - Log.error("Failed to bind Jetty", e); - } catch (Exception e) { - Log.error("Failed to start Jetty", e); - } + public void stop() { + try { + jettyServer.stop(); + } catch (Exception e) { + Log.error("Failed to stop Jetty", e); } + } - public void stop() { - try { - jettyServer.stop(); - } catch (Exception e) { - Log.error("Failed to stop Jetty", e); - } - } - - private void configureJettyServer( - Config config, - RepoStore repoStore, - SnapshotApi snapshotApi - ) throws ServletException { - ServerConnector connector = new ServerConnector(this.jettyServer); - connector.setPort(config.getPort()); - connector.setHost(config.getBindIp()); - connector.setIdleTimeout(config.getIdleTimeout()); - this.jettyServer.addConnector(connector); - - HandlerCollection handlers = new HandlerList(); - handlers.addHandler(initApiHandler()); - handlers.addHandler(initBaseHandler()); - handlers.addHandler(initGitHandler(config, repoStore, snapshotApi)); - jettyServer.setHandler(handlers); - } - - private Handler initBaseHandler() { - ContextHandler base = new ContextHandler(); - base.setContextPath("/"); - HandlerCollection handlers = new HandlerList(); - handlers.addHandler(new StatusHandler(bridge)); - handlers.addHandler(new HealthCheckHandler(bridge)); - handlers.addHandler(new GitLfsHandler(bridge)); - handlers.addHandler(new PrometheusHandler()); - handlers.addHandler(new DiagnosticsHandler()); - base.setHandler(handlers); - return base; - } - - private Handler initApiHandler() { - ContextHandler api = new ContextHandler(); - api.setContextPath("/api"); - - HandlerCollection handlers = new HandlerList(); - handlers.addHandler(initResourceHandler()); - handlers.addHandler(new PostbackHandler(bridge)); - handlers.addHandler(new ProjectDeletionHandler(bridge)); - handlers.addHandler(new DefaultHandler()); - - api.setHandler(handlers); - - ProductionErrorHandler errorHandler = new ProductionErrorHandler(); - api.setErrorHandler(errorHandler); - return api; - } - - private Handler initGitHandler( - Config config, - RepoStore repoStore, - SnapshotApi snapshotApi - ) throws ServletException { - final ServletContextHandler servletContextHandler = - new ServletContextHandler(ServletContextHandler.SESSIONS); - if (config.isUsingOauth2()) { - Filter filter = new Oauth2Filter( - snapshotApi, - config.getOauth2(), - config.isUserPasswordEnabled() - ); - servletContextHandler.addFilter( - new FilterHolder(filter), - "/*", - EnumSet.of(DispatcherType.REQUEST) - ); - } - servletContextHandler.setContextPath("/"); - servletContextHandler.addServlet( - new ServletHolder( - new WLGitServlet( - servletContextHandler, - repoStore, - bridge - ) - ), - "/*" - ); - ProductionErrorHandler errorHandler = new ProductionErrorHandler(); - servletContextHandler.setErrorHandler(errorHandler); - return servletContextHandler; - } - - private Handler initResourceHandler() { - ResourceHandler resourceHandler = new FileHandler(bridge); - resourceHandler.setResourceBase( - new File(rootGitDirectoryPath, ".wlgb/atts").getAbsolutePath() - ); - return resourceHandler; + private void configureJettyServer(Config config, RepoStore repoStore, SnapshotApi snapshotApi) + throws ServletException { + ServerConnector connector = new ServerConnector(this.jettyServer); + connector.setPort(config.getPort()); + connector.setHost(config.getBindIp()); + connector.setIdleTimeout(config.getIdleTimeout()); + this.jettyServer.addConnector(connector); + + HandlerCollection handlers = new HandlerList(); + handlers.addHandler(initApiHandler()); + handlers.addHandler(initBaseHandler()); + handlers.addHandler(initGitHandler(config, repoStore, snapshotApi)); + jettyServer.setHandler(handlers); + } + + private Handler initBaseHandler() { + ContextHandler base = new ContextHandler(); + base.setContextPath("/"); + HandlerCollection handlers = new HandlerList(); + handlers.addHandler(new StatusHandler(bridge)); + handlers.addHandler(new HealthCheckHandler(bridge)); + handlers.addHandler(new GitLfsHandler(bridge)); + handlers.addHandler(new PrometheusHandler()); + handlers.addHandler(new DiagnosticsHandler()); + base.setHandler(handlers); + return base; + } + + private Handler initApiHandler() { + ContextHandler api = new ContextHandler(); + api.setContextPath("/api"); + + HandlerCollection handlers = new HandlerList(); + handlers.addHandler(initResourceHandler()); + handlers.addHandler(new PostbackHandler(bridge)); + handlers.addHandler(new ProjectDeletionHandler(bridge)); + handlers.addHandler(new DefaultHandler()); + + api.setHandler(handlers); + + ProductionErrorHandler errorHandler = new ProductionErrorHandler(); + api.setErrorHandler(errorHandler); + return api; + } + + private Handler initGitHandler(Config config, RepoStore repoStore, SnapshotApi snapshotApi) + throws ServletException { + final ServletContextHandler servletContextHandler = + new ServletContextHandler(ServletContextHandler.SESSIONS); + if (config.isUsingOauth2()) { + Filter filter = + new Oauth2Filter(snapshotApi, config.getOauth2(), config.isUserPasswordEnabled()); + servletContextHandler.addFilter( + new FilterHolder(filter), "/*", EnumSet.of(DispatcherType.REQUEST)); } + servletContextHandler.setContextPath("/"); + servletContextHandler.addServlet( + new ServletHolder(new WLGitServlet(servletContextHandler, repoStore, bridge)), "/*"); + ProductionErrorHandler errorHandler = new ProductionErrorHandler(); + servletContextHandler.setErrorHandler(errorHandler); + return servletContextHandler; + } + private Handler initResourceHandler() { + ResourceHandler resourceHandler = new FileHandler(bridge); + resourceHandler.setResourceBase(new File(rootGitDirectoryPath, ".wlgb/atts").getAbsolutePath()); + return resourceHandler; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitLfsHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitLfsHandler.java index 3f1b25eca1..67ab2b3593 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitLfsHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitLfsHandler.java @@ -1,18 +1,12 @@ package uk.ac.ic.wlgitbridge.server; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.util.Log; - -import javax.servlet.ServletException; +import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import uk.ac.ic.wlgitbridge.bridge.Bridge; +import uk.ac.ic.wlgitbridge.util.Log; public class GitLfsHandler extends AbstractHandler { @@ -24,23 +18,19 @@ public class GitLfsHandler extends AbstractHandler { @Override public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException { String method = baseRequest.getMethod(); - if ( - ("POST".equals(method)) + if (("POST".equals(method)) && target != null - && target.matches("^/[0-9a-z]+\\.git/info/lfs/objects/batch/?$") - ) { + && target.matches("^/[0-9a-z]+\\.git/info/lfs/objects/batch/?$")) { Log.debug(method + " <- /.git/info/lfs/objects/batch"); response.setContentType("application/vnd.git-lfs+json"); response.setStatus(422); - response.getWriter().println("{\"message\": \"ERROR: Git LFS is not supported on Overleaf\"}"); + response + .getWriter() + .println("{\"message\": \"ERROR: Git LFS is not supported on Overleaf\"}"); baseRequest.setHandled(true); } } - } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/HealthCheckHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/HealthCheckHandler.java index fe27a6d94c..fe2ad43d65 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/HealthCheckHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/HealthCheckHandler.java @@ -1,17 +1,12 @@ package uk.ac.ic.wlgitbridge.server; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.util.Log; - -import javax.servlet.ServletException; +import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import uk.ac.ic.wlgitbridge.bridge.Bridge; +import uk.ac.ic.wlgitbridge.util.Log; public class HealthCheckHandler extends AbstractHandler { @@ -23,17 +18,12 @@ public class HealthCheckHandler extends AbstractHandler { @Override public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException { String method = baseRequest.getMethod(); - if ( - ("GET".equals(method) || "HEAD".equals(method)) + if (("GET".equals(method) || "HEAD".equals(method)) && target != null - && target.matches("^/health_check/?$") - ) { + && target.matches("^/health_check/?$")) { Log.debug(method + " <- /health_check"); baseRequest.setHandled(true); response.setContentType("text/plain"); @@ -46,5 +36,4 @@ public class HealthCheckHandler extends AbstractHandler { } } } - } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/Oauth2Filter.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/Oauth2Filter.java index 397e9c0077..5fcc33ecaa 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/Oauth2Filter.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/Oauth2Filter.java @@ -5,397 +5,336 @@ import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpHeaders; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpResponse; -import org.apache.commons.codec.binary.Base64; -import org.eclipse.jetty.server.Request; -import uk.ac.ic.wlgitbridge.application.config.Oauth2; -import uk.ac.ic.wlgitbridge.bridge.snapshot.SnapshotApi; -import uk.ac.ic.wlgitbridge.snapshot.base.MissingRepositoryException; -import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException; -import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocRequest; -import uk.ac.ic.wlgitbridge.util.Instance; -import uk.ac.ic.wlgitbridge.util.Log; -import uk.ac.ic.wlgitbridge.util.Util; - -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.*; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.codec.binary.Base64; +import uk.ac.ic.wlgitbridge.application.config.Oauth2; +import uk.ac.ic.wlgitbridge.bridge.snapshot.SnapshotApi; +import uk.ac.ic.wlgitbridge.util.Instance; +import uk.ac.ic.wlgitbridge.util.Log; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by winston on 25/10/15. */ public class Oauth2Filter implements Filter { - public static final String ATTRIBUTE_KEY = "oauth2"; + public static final String ATTRIBUTE_KEY = "oauth2"; - private final SnapshotApi snapshotApi; + private final SnapshotApi snapshotApi; - private final Oauth2 oauth2; + private final Oauth2 oauth2; - private final boolean isUserPasswordEnabled; + private final boolean isUserPasswordEnabled; - public Oauth2Filter(SnapshotApi snapshotApi, Oauth2 oauth2, boolean isUserPasswordEnabled) { - this.snapshotApi = snapshotApi; - this.oauth2 = oauth2; - this.isUserPasswordEnabled = isUserPasswordEnabled; + public Oauth2Filter(SnapshotApi snapshotApi, Oauth2 oauth2, boolean isUserPasswordEnabled) { + this.snapshotApi = snapshotApi; + this.oauth2 = oauth2; + this.isUserPasswordEnabled = isUserPasswordEnabled; + } + + @Override + public void init(FilterConfig filterConfig) {} + + /* + * The original request from git will not contain the Authorization header. + * + * So, for projects that need auth, we return 401. Git will swallow this + * and prompt the user for user/pass, and then make a brand new request. + * @param servletRequest + * @param servletResponse + * @param filterChain + * @throws IOException + * @throws ServletException + */ + @Override + public void doFilter( + ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + String requestUri = request.getRequestURI(); + + if (requestUri.startsWith("/project")) { + Log.info("[{}] Invalid request URI", requestUri); + sendResponse( + response, 404, Arrays.asList("Invalid Project ID (must not have a '/project' prefix)")); + return; } - @Override - public void init(FilterConfig filterConfig) {} + String projectId = Util.removeAllSuffixes(requestUri.split("/")[1], ".git"); - /** - * The original request from git will not contain the Authorization header. - * - * So, for projects that need auth, we return 401. Git will swallow this - * and prompt the user for user/pass, and then make a brand new request. - * @param servletRequest - * @param servletResponse - * @param filterChain - * @throws IOException - * @throws ServletException - */ - @Override - public void doFilter( - ServletRequest servletRequest, - ServletResponse servletResponse, - FilterChain filterChain - ) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - String requestUri = request.getRequestURI(); + BasicAuthCredentials basicAuthCreds = getBasicAuthCredentials(request); + if (basicAuthCreds == null) { + handleNeedAuthorization(projectId, "(unknown)", request, response); + return; + } + String username = basicAuthCreds.getUsername(); + String password = basicAuthCreds.getPassword(); - if (requestUri.startsWith("/project")) { - Log.info("[{}] Invalid request URI", requestUri); - sendResponse(response,404, Arrays.asList( - "Invalid Project ID (must not have a '/project' prefix)" - )); - return; - } + if (isLinkSharingId(projectId)) { + handleLinkSharingId(projectId, username, request, response); + return; + } + if (!isProjectId(projectId)) { + handleBadProjectId(projectId, username, request, response); + return; + } - String projectId = Util.removeAllSuffixes(requestUri.split("/")[1], ".git"); + final Credential cred = + new Credential.Builder(BearerToken.authorizationHeaderAccessMethod()).build(); - BasicAuthCredentials basicAuthCreds = getBasicAuthCredentials(request); - if (basicAuthCreds == null) { - handleNeedAuthorization(projectId, "(unknown)", request, response); - return; - } - String username = basicAuthCreds.getUsername(); - String password = basicAuthCreds.getPassword(); + if (username.equals("git")) { + Log.debug("[{}] username is 'git', skipping password grant flow", projectId); - if (isLinkSharingId(projectId)) { - handleLinkSharingId(projectId, username, request, response); - return; - } - if (!isProjectId(projectId)) { - handleBadProjectId(projectId, username, request, response); - return; - } - - final Credential cred = new Credential.Builder( - BearerToken.authorizationHeaderAccessMethod() - ).build(); - - if (username.equals("git")) { - Log.debug("[{}] username is 'git', skipping password grant flow", projectId); - - // Check that the access token is valid. In principle, we could - // wait until we make the actual request to the web api, but the - // JGit API doesn't make it easy to reply with a 401 and a custom - // error message. This is something we can do in this filter, so as - // a workaround, we use the /oauth/token/info endpoint to verify - // the access token. - // - // It's still theoretically possible for the web api request to - // fail later (for example, in the unlikely event that the token - // expired between the two requests). In that case, JGit will - // return a 401 without a custom error message. - int statusCode = checkAccessToken(oauth2, password, getClientIp(request)); - if (statusCode == 429) { - handleRateLimit(projectId, username, request, response); - return; - } else if (statusCode == 401) { - handleBadAccessToken(projectId, request, response); - return; - } else if (statusCode >= 400) { - handleUnknownOauthServerError(projectId, statusCode, request, response); - return; - } - cred.setAccessToken(password); - } else if (this.isUserPasswordEnabled) { - String accessToken = null; - try { - accessToken = doPasswordGrantFlow(username, password, getClientIp(request)); - } catch (TokenResponseException e) { - int statusCode = e.getStatusCode(); - if (statusCode == 429) { - handleRateLimit(projectId, username, request, response); - } else if (statusCode == 400 || statusCode == 401) { - handleNeedAuthorization(projectId, username, request, response); - } else { - handleUnknownOauthServerError(projectId, statusCode, request, response); - } - return; - } - cred.setAccessToken(accessToken); + // Check that the access token is valid. In principle, we could + // wait until we make the actual request to the web api, but the + // JGit API doesn't make it easy to reply with a 401 and a custom + // error message. This is something we can do in this filter, so as + // a workaround, we use the /oauth/token/info endpoint to verify + // the access token. + // + // It's still theoretically possible for the web api request to + // fail later (for example, in the unlikely event that the token + // expired between the two requests). In that case, JGit will + // return a 401 without a custom error message. + int statusCode = checkAccessToken(oauth2, password, getClientIp(request)); + if (statusCode == 429) { + handleRateLimit(projectId, username, request, response); + return; + } else if (statusCode == 401) { + handleBadAccessToken(projectId, request, response); + return; + } else if (statusCode >= 400) { + handleUnknownOauthServerError(projectId, statusCode, request, response); + return; + } + cred.setAccessToken(password); + } else if (this.isUserPasswordEnabled) { + String accessToken = null; + try { + accessToken = doPasswordGrantFlow(username, password, getClientIp(request)); + } catch (TokenResponseException e) { + int statusCode = e.getStatusCode(); + if (statusCode == 429) { + handleRateLimit(projectId, username, request, response); + } else if (statusCode == 400 || statusCode == 401) { + handleNeedAuthorization(projectId, username, request, response); } else { - handleNeedAuthorization(projectId, username, request, response); - return; + handleUnknownOauthServerError(projectId, statusCode, request, response); } - - servletRequest.setAttribute(ATTRIBUTE_KEY, cred); - filterChain.doFilter( - servletRequest, - servletResponse - ); + return; + } + cred.setAccessToken(accessToken); + } else { + handleNeedAuthorization(projectId, username, request, response); + return; } - @Override - public void destroy() {} + servletRequest.setAttribute(ATTRIBUTE_KEY, cred); + filterChain.doFilter(servletRequest, servletResponse); + } - private boolean isLinkSharingId(String projectId) { - return projectId.matches("^[0-9]+[bcdfghjklmnpqrstvwxyz]{6,12}$"); + @Override + public void destroy() {} + + private boolean isLinkSharingId(String projectId) { + return projectId.matches("^[0-9]+[bcdfghjklmnpqrstvwxyz]{6,12}$"); + } + + private boolean isProjectId(String projectId) { + return projectId.matches("^[0-9a-f]{24}$"); + } + + private void sendResponse(HttpServletResponse response, int code, List lines) + throws IOException { + response.setContentType("text/plain"); + response.setStatus(code); + PrintWriter w = response.getWriter(); + for (String line : lines) { + w.println(line); } + w.close(); + } - private boolean isProjectId(String projectId) { - return projectId.matches("^[0-9a-f]{24}$"); - } - - private void sendResponse(HttpServletResponse response, int code, List lines) throws IOException { - response.setContentType("text/plain"); - response.setStatus(code); - PrintWriter w = response.getWriter(); - for (String line : lines) { - w.println(line); - } - w.close(); - } - - private void handleLinkSharingId( - String projectId, - String username, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - Log.info( - "[{}] Bad project id, User '{}' ip={}", - projectId, - username, - getClientIp(request) - ); - sendResponse(response, 404, Arrays.asList( + private void handleLinkSharingId( + String projectId, String username, HttpServletRequest request, HttpServletResponse response) + throws IOException { + Log.info("[{}] Bad project id, User '{}' ip={}", projectId, username, getClientIp(request)); + sendResponse( + response, + 404, + Arrays.asList( "Git access via link sharing link is not supported.", "", "You can find the project's git remote url by opening it in your browser", "and selecting Git from the left sidebar in the project view.", "", "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - )); - } + "see https://www.overleaf.com/learn/how-to/Git_integration for more information.")); + } - private void handleBadProjectId( - String projectId, - String username, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - Log.info( - "[{}] Bad project id, User '{}' ip={}", - projectId, - username, - getClientIp(request) - ); - sendResponse(response, 404, Arrays.asList( + private void handleBadProjectId( + String projectId, String username, HttpServletRequest request, HttpServletResponse response) + throws IOException { + Log.info("[{}] Bad project id, User '{}' ip={}", projectId, username, getClientIp(request)); + sendResponse( + response, + 404, + Arrays.asList( "This Overleaf project does not exist.", "", "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - )); - } + "see https://www.overleaf.com/learn/how-to/Git_integration for more information.")); + } - private void handleRateLimit( - String projectId, - String username, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - Log.info( - "[{}] Rate limit, User '{}' ip={}", - projectId, - username, - getClientIp(request) - ); - sendResponse(response, 429, Arrays.asList( - "Rate limit exceeded. Please wait and try again later." - )); - } + private void handleRateLimit( + String projectId, String username, HttpServletRequest request, HttpServletResponse response) + throws IOException { + Log.info("[{}] Rate limit, User '{}' ip={}", projectId, username, getClientIp(request)); + sendResponse( + response, 429, Arrays.asList("Rate limit exceeded. Please wait and try again later.")); + } - private void handleNeedAuthorization( - String projectId, - String username, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - Log.info( - "[{}] Unauthorized, User '{}' ip={}", - projectId, - username, - getClientIp(request) - ); - response.setHeader("WWW-Authenticate", "Basic realm=\"Git Bridge\""); - if (this.isUserPasswordEnabled) { - sendResponse(response, 401, Arrays.asList( - "Log in using the email address and password you use for Overleaf.", - "", - "*Note*: if you use a provider such as Google or Twitter to sign into", - "your Overleaf account, you will need to set a password.", - "", - "See our help page for more support:", - "https://www.overleaf.com/learn/how-to/Troubleshooting_git_bridge_problems" - )); - } else { - sendResponse(response, 401, Arrays.asList( - "Log in with the username 'git' and enter your Git authentication token", - "when prompted for a password.", - "", - "You can generate and manage your Git authentication tokens in", - "your Overleaf Account Settings." - )); - } + private void handleNeedAuthorization( + String projectId, String username, HttpServletRequest request, HttpServletResponse response) + throws IOException { + Log.info("[{}] Unauthorized, User '{}' ip={}", projectId, username, getClientIp(request)); + response.setHeader("WWW-Authenticate", "Basic realm=\"Git Bridge\""); + if (this.isUserPasswordEnabled) { + sendResponse( + response, + 401, + Arrays.asList( + "Log in using the email address and password you use for Overleaf.", + "", + "*Note*: if you use a provider such as Google or Twitter to sign into", + "your Overleaf account, you will need to set a password.", + "", + "See our help page for more support:", + "https://www.overleaf.com/learn/how-to/Troubleshooting_git_bridge_problems")); + } else { + sendResponse( + response, + 401, + Arrays.asList( + "Log in with the username 'git' and enter your Git authentication token", + "when prompted for a password.", + "", + "You can generate and manage your Git authentication tokens in", + "your Overleaf Account Settings.")); } + } - private void handleBadAccessToken( - String projectId, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - Log.info( - "[{}] Bad access token, ip={}", - projectId, - getClientIp(request) - ); - sendResponse(response, 401, Arrays.asList( + private void handleBadAccessToken( + String projectId, HttpServletRequest request, HttpServletResponse response) + throws IOException { + Log.info("[{}] Bad access token, ip={}", projectId, getClientIp(request)); + sendResponse( + response, + 401, + Arrays.asList( "Enter your Git authentication token when prompted for a password.", "", "You can generate and manage your Git authentication tokens in", - "your Overleaf Account Settings." - )); + "your Overleaf Account Settings.")); + } + + private int checkAccessToken(Oauth2 oauth2, String accessToken, String clientIp) + throws IOException { + GenericUrl url = + new GenericUrl(oauth2.getOauth2Server() + "/oauth/token/info?client_ip=" + clientIp); + HttpRequest request = Instance.httpRequestFactory.buildGetRequest(url); + HttpHeaders headers = new HttpHeaders(); + headers.setAuthorization("Bearer " + accessToken); + request.setHeaders(headers); + request.setThrowExceptionOnExecuteError(false); + HttpResponse response = request.execute(); + int statusCode = response.getStatusCode(); + response.disconnect(); + return statusCode; + } + + private void handleUnknownOauthServerError( + String projectId, int statusCode, HttpServletRequest request, HttpServletResponse response) + throws IOException { + Log.info( + "[{}] OAuth server error, statusCode={}, ip={}", + projectId, + statusCode, + getClientIp(request)); + sendResponse(response, 500, Arrays.asList("Unexpected server error. Please try again later.")); + } + + /* + * Gets the remote IP from the request. + */ + private String getClientIp(HttpServletRequest request) { + String clientIp = request.getHeader("X-Forwarded-For"); + if (clientIp == null) { + clientIp = request.getRemoteAddr(); + } + return clientIp; + } + + /* + * Extract basic auth credentials from the request. + * + * Returns null if valid basic auth credentials couldn't be found. + */ + private BasicAuthCredentials getBasicAuthCredentials(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + if (authHeader == null) { + return null; } - private int checkAccessToken( - Oauth2 oauth2, - String accessToken, - String clientIp - ) throws IOException { - GenericUrl url = new GenericUrl( - oauth2.getOauth2Server() + "/oauth/token/info?client_ip=" + clientIp - ); - HttpRequest request = Instance.httpRequestFactory.buildGetRequest(url); - HttpHeaders headers = new HttpHeaders(); - headers.setAuthorization("Bearer " + accessToken); - request.setHeaders(headers); - request.setThrowExceptionOnExecuteError(false); - HttpResponse response = request.execute(); - int statusCode = response.getStatusCode(); - response.disconnect(); - return statusCode; + StringTokenizer st = new StringTokenizer(authHeader); + if (!st.hasMoreTokens()) { + return null; + } + String basic = st.nextToken(); + if (!basic.equalsIgnoreCase("Basic")) { + return null; } - private void handleUnknownOauthServerError( - String projectId, - int statusCode, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - Log.info( - "[{}] OAuth server error, statusCode={}, ip={}", - projectId, - statusCode, - getClientIp(request) - ); - sendResponse(response, 500, Arrays.asList( - "Unexpected server error. Please try again later." - )); + String credentials = null; + try { + credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8"); + } catch (UnsupportedEncodingException e) { + return null; } - /** - * Gets the remote IP from the request. - */ - private String getClientIp(HttpServletRequest request) { - String clientIp = request.getHeader("X-Forwarded-For"); - if (clientIp == null) { - clientIp = request.getRemoteAddr(); - } - return clientIp; + String[] split = credentials.split(":", 2); + if (split.length != 2) { + return null; } + String username = split[0]; + String password = split[1]; + return new BasicAuthCredentials(username, password); + } - /** - * Extract basic auth credentials from the request. - * - * Returns null if valid basic auth credentials couldn't be found. - */ - private BasicAuthCredentials getBasicAuthCredentials(HttpServletRequest request) { - String authHeader = request.getHeader("Authorization"); - if (authHeader == null) { - return null; - } - - StringTokenizer st = new StringTokenizer(authHeader); - if (!st.hasMoreTokens()) { - return null; - } - String basic = st.nextToken(); - if (!basic.equalsIgnoreCase("Basic")) { - return null; - } - - String credentials = null; - try { - credentials = new String( - Base64.decodeBase64(st.nextToken()), - "UTF-8" - ); - } catch (UnsupportedEncodingException e) { - return null; - } - - String[] split = credentials.split(":", 2); - if (split.length != 2) { - return null; - } - String username = split[0]; - String password = split[1]; - return new BasicAuthCredentials(username, password); - } - - /** - * Perform a password grant flow with the OAuth server and return an access token. - * - * The access token is null if the password grant flow was unsuccessful. - */ - private String doPasswordGrantFlow( - String username, - String password, - String clientIp - ) throws IOException { - return new PasswordTokenRequest( + /* + * Perform a password grant flow with the OAuth server and return an access token. + * + * The access token is null if the password grant flow was unsuccessful. + */ + private String doPasswordGrantFlow(String username, String password, String clientIp) + throws IOException { + return new PasswordTokenRequest( Instance.httpTransport, Instance.jsonFactory, - new GenericUrl( - oauth2.getOauth2Server() - + "/oauth/token?client_ip=" - + clientIp - ), + new GenericUrl(oauth2.getOauth2Server() + "/oauth/token?client_ip=" + clientIp), username, - password - ).setClientAuthentication( + password) + .setClientAuthentication( new ClientParametersAuthentication( - oauth2.getOauth2ClientID(), - oauth2.getOauth2ClientSecret() - ) - ).execute().getAccessToken(); - } + oauth2.getOauth2ClientID(), oauth2.getOauth2ClientSecret())) + .execute() + .getAccessToken(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackContents.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackContents.java index 3ef85620b0..f925975302 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackContents.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackContents.java @@ -4,85 +4,68 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostExceptionBuilder; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException; import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 17/11/14. */ public class PostbackContents implements JSONSource { - private static final String CODE_SUCCESS = "upToDate"; + private static final String CODE_SUCCESS = "upToDate"; - private final Bridge bridge; - private final String projectName; - private final String postbackKey; + private final Bridge bridge; + private final String projectName; + private final String postbackKey; - private final SnapshotPostExceptionBuilder snapshotPostExceptionBuilder; + private final SnapshotPostExceptionBuilder snapshotPostExceptionBuilder; - private int versionID; - private SnapshotPostException exception; + private int versionID; + private SnapshotPostException exception; - public PostbackContents( - Bridge bridge, - String projectName, - String postbackKey, - String contents - ) { - this.bridge = bridge; - this.projectName = projectName; - this.postbackKey = postbackKey; - snapshotPostExceptionBuilder = new SnapshotPostExceptionBuilder(); - fromJSON(new Gson().fromJson(contents, JsonElement.class)); + public PostbackContents(Bridge bridge, String projectName, String postbackKey, String contents) { + this.bridge = bridge; + this.projectName = projectName; + this.postbackKey = postbackKey; + snapshotPostExceptionBuilder = new SnapshotPostExceptionBuilder(); + fromJSON(new Gson().fromJson(contents, JsonElement.class)); + } + + @Override + public void fromJSON(JsonElement json) { + JsonObject responseObject = json.getAsJsonObject(); + String code = Util.getCodeFromResponse(responseObject); + setResult(responseObject, code); + } + + public void processPostback() throws UnexpectedPostbackException { + if (exception == null) { + bridge.postbackReceivedSuccessfully(projectName, postbackKey, versionID); + } else { + bridge.postbackReceivedWithException(projectName, postbackKey, exception); } + } - @Override - public void fromJSON(JsonElement json) { - JsonObject responseObject = json.getAsJsonObject(); - String code = Util.getCodeFromResponse(responseObject); - setResult(responseObject, code); + private void setResult(JsonObject responseObject, String code) { + if (code.equals(CODE_SUCCESS)) { + setVersionID(responseObject); + } else { + setException(responseObject, code); } + } - public void processPostback() throws UnexpectedPostbackException { - if (exception == null) { - bridge.postbackReceivedSuccessfully( - projectName, - postbackKey, - versionID - ); - } else { - bridge.postbackReceivedWithException( - projectName, - postbackKey, - exception - ); - } + private void setVersionID(JsonObject responseObject) { + versionID = responseObject.get("latestVerId").getAsInt(); + } + + private void setException(JsonObject responseObject, String code) { + try { + exception = snapshotPostExceptionBuilder.build(code, responseObject); + } catch (UnexpectedPostbackException e) { + throw new RuntimeException(e); } - - private void setResult(JsonObject responseObject, String code) { - if (code.equals(CODE_SUCCESS)) { - setVersionID(responseObject); - } else { - setException(responseObject, code); - } - } - - private void setVersionID(JsonObject responseObject) { - versionID = responseObject.get("latestVerId").getAsInt(); - } - - private void setException(JsonObject responseObject, String code) { - try { - exception = snapshotPostExceptionBuilder.build( - code, - responseObject - ); - } catch (UnexpectedPostbackException e) { - throw new RuntimeException(e); - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackHandler.java index c4dd71c325..a121844ae1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackHandler.java @@ -2,6 +2,10 @@ package uk.ac.ic.wlgitbridge.server; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import uk.ac.ic.wlgitbridge.bridge.Bridge; @@ -9,90 +13,59 @@ import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException; import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.Util; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** +/* * Created by Winston on 16/11/14. */ public class PostbackHandler extends AbstractHandler { - private final Bridge bridge; + private final Bridge bridge; - public PostbackHandler(Bridge bridge) { - this.bridge = bridge; - } + public PostbackHandler(Bridge bridge) { + this.bridge = bridge; + } - @Override - public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException, ServletException { - Log.debug( - "PostbackHandler: " + baseRequest.getMethod() - + " <- " - + baseRequest.getHttpURI() - ); - try { - if ( - request.getMethod().equals("POST") - && target.endsWith("postback") - ) { - response.setContentType("application/json"); - String contents = Util.getContentsOfReader(request.getReader()); - String[] parts = target.split("/"); - if (parts.length < 4) { - throw new ServletException(); - } - String projectName = parts[1]; - String postbackKey = parts[2]; - PostbackContents postbackContents = new PostbackContents( - bridge, - projectName, - postbackKey, - contents - ); - JsonObject body = new JsonObject(); - - try { - postbackContents.processPostback(); - } catch (UnexpectedPostbackException e) { - response.setStatus(HttpServletResponse.SC_CONFLICT); - body.add("code", new JsonPrimitive("unexpectedPostback")); - response.getWriter().println(body); - baseRequest.setHandled(true); - return; - } - response.setStatus(HttpServletResponse.SC_OK); - body.add("code", new JsonPrimitive("success")); - response.getWriter().println(body); - baseRequest.setHandled(true); - } - } catch (IOException e) { - Log.warn( - "IOException when handling postback to target: " + target, - e - ); - throw e; - } catch (ServletException e) { - Log.warn( - "ServletException when handling postback to target: " + - target, - e - ); - throw e; - } catch (RuntimeException e) { - Log.warn( - "RuntimeException when handling postback to target: " + - target, - e - ); - throw e; + @Override + public void handle( + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + Log.debug("PostbackHandler: " + baseRequest.getMethod() + " <- " + baseRequest.getHttpURI()); + try { + if (request.getMethod().equals("POST") && target.endsWith("postback")) { + response.setContentType("application/json"); + String contents = Util.getContentsOfReader(request.getReader()); + String[] parts = target.split("/"); + if (parts.length < 4) { + throw new ServletException(); } - } + String projectName = parts[1]; + String postbackKey = parts[2]; + PostbackContents postbackContents = + new PostbackContents(bridge, projectName, postbackKey, contents); + JsonObject body = new JsonObject(); + try { + postbackContents.processPostback(); + } catch (UnexpectedPostbackException e) { + response.setStatus(HttpServletResponse.SC_CONFLICT); + body.add("code", new JsonPrimitive("unexpectedPostback")); + response.getWriter().println(body); + baseRequest.setHandled(true); + return; + } + response.setStatus(HttpServletResponse.SC_OK); + body.add("code", new JsonPrimitive("success")); + response.getWriter().println(body); + baseRequest.setHandled(true); + } + } catch (IOException e) { + Log.warn("IOException when handling postback to target: " + target, e); + throw e; + } catch (ServletException e) { + Log.warn("ServletException when handling postback to target: " + target, e); + throw e; + } catch (RuntimeException e) { + Log.warn("RuntimeException when handling postback to target: " + target, e); + throw e; + } + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProductionErrorHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProductionErrorHandler.java index d4b19e26fe..ca21a82b7a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProductionErrorHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProductionErrorHandler.java @@ -1,21 +1,22 @@ package uk.ac.ic.wlgitbridge.server; +import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.handler.ErrorHandler; -import java.io.IOException; public class ProductionErrorHandler extends ErrorHandler { - @Override - public void handle( - String target, - org.eclipse.jetty.server.Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - response.getWriter() - .append("{\"message\":\"HTTP error ") - .append(String.valueOf(response.getStatus())) - .append("\"}"); - } + @Override + public void handle( + String target, + org.eclipse.jetty.server.Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) + throws IOException { + response + .getWriter() + .append("{\"message\":\"HTTP error ") + .append(String.valueOf(response.getStatus())) + .append("\"}"); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProjectDeletionHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProjectDeletionHandler.java index c47ecb6c61..731a75ee2e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProjectDeletionHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProjectDeletionHandler.java @@ -1,13 +1,12 @@ package uk.ac.ic.wlgitbridge.server; import java.io.IOException; -import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.Request; - +import org.eclipse.jetty.server.handler.AbstractHandler; import uk.ac.ic.wlgitbridge.bridge.Bridge; public class ProjectDeletionHandler extends AbstractHandler { @@ -21,11 +20,8 @@ public class ProjectDeletionHandler extends AbstractHandler { @Override public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException { String method = baseRequest.getMethod(); Matcher matcher = routePattern.matcher(target); if (method.equals("DELETE") && target != null && matcher.matches()) { diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PrometheusHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PrometheusHandler.java index 74e4d66477..315b458d73 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PrometheusHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PrometheusHandler.java @@ -3,19 +3,6 @@ package uk.ac.ic.wlgitbridge.server; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.common.TextFormat; import io.prometheus.client.hotspot.DefaultExports; - -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.util.Log; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; @@ -23,6 +10,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import uk.ac.ic.wlgitbridge.util.Log; public class PrometheusHandler extends AbstractHandler { @@ -32,27 +25,18 @@ public class PrometheusHandler extends AbstractHandler { @Override public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException, ServletException { + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { String method = baseRequest.getMethod(); - if ( - ("GET".equals(method)) - && target != null - && target.matches("^/metrics/?$") - ) { + if (("GET".equals(method)) && target != null && target.matches("^/metrics/?$")) { Log.debug(method + " <- /metrics"); this.printMetrics(request, response); baseRequest.setHandled(true); } } - private void printMetrics( - HttpServletRequest request, - HttpServletResponse response - ) throws ServletException, IOException { + private void printMetrics(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { response.setStatus(200); String contentType = TextFormat.chooseContentType(request.getHeader("Accept")); response.setContentType(contentType); @@ -60,7 +44,10 @@ public class PrometheusHandler extends AbstractHandler { Writer writer = new BufferedWriter(response.getWriter()); try { - TextFormat.writeFormat(contentType, writer, CollectorRegistry.defaultRegistry.filteredMetricFamilySamples(parse(request))); + TextFormat.writeFormat( + contentType, + writer, + CollectorRegistry.defaultRegistry.filteredMetricFamilySamples(parse(request))); writer.flush(); } finally { writer.close(); diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/StatusHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/StatusHandler.java index 9ffb37c6c6..6750fa0735 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/StatusHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/StatusHandler.java @@ -1,18 +1,12 @@ package uk.ac.ic.wlgitbridge.server; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import uk.ac.ic.wlgitbridge.bridge.Bridge; -import uk.ac.ic.wlgitbridge.util.Log; - -import javax.servlet.ServletException; +import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import uk.ac.ic.wlgitbridge.bridge.Bridge; +import uk.ac.ic.wlgitbridge.util.Log; public class StatusHandler extends AbstractHandler { @@ -24,17 +18,12 @@ public class StatusHandler extends AbstractHandler { @Override public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException { String method = baseRequest.getMethod(); - if ( - ("GET".equals(method) || "HEAD".equals(method)) + if (("GET".equals(method) || "HEAD".equals(method)) && target != null - && target.matches("^/status/?$") - ) { + && target.matches("^/status/?$")) { Log.debug(method + " <- /status"); baseRequest.setHandled(true); response.setContentType("text/plain"); @@ -42,5 +31,4 @@ public class StatusHandler extends AbstractHandler { response.getWriter().println("ok"); } } - } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/ForbiddenException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/ForbiddenException.java index 726278aaad..9068223832 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/ForbiddenException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/ForbiddenException.java @@ -1,27 +1,25 @@ package uk.ac.ic.wlgitbridge.snapshot.base; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; - import java.util.Arrays; import java.util.List; +import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; -/** +/* * Created by winston on 25/10/15. */ public class ForbiddenException extends SnapshotAPIException { - @Override - public void fromJSON(JsonElement json) {} + @Override + public void fromJSON(JsonElement json) {} - @Override - public String getMessage() { - return "forbidden"; - } - - @Override - public List getDescriptionLines() { - return Arrays.asList(getMessage()); - } + @Override + public String getMessage() { + return "forbidden"; + } + @Override + public List getDescriptionLines() { + return Arrays.asList(getMessage()); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/HTTPMethod.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/HTTPMethod.java index 6fdf488483..30bc2d5238 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/HTTPMethod.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/HTTPMethod.java @@ -1,11 +1,9 @@ package uk.ac.ic.wlgitbridge.snapshot.base; -/** +/* * Created by Winston on 16/11/14. */ public enum HTTPMethod { - - POST, - GET - + POST, + GET } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/JSONSource.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/JSONSource.java index e1a13ac5bb..764384bd67 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/JSONSource.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/JSONSource.java @@ -2,11 +2,10 @@ package uk.ac.ic.wlgitbridge.snapshot.base; import com.google.gson.JsonElement; -/** +/* * Created by Winston on 06/11/14. */ public interface JSONSource { - void fromJSON(JsonElement json); - + void fromJSON(JsonElement json); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/MissingRepositoryException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/MissingRepositoryException.java index e1a2b8752a..5b26c4e433 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/MissingRepositoryException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/MissingRepositoryException.java @@ -1,99 +1,93 @@ package uk.ac.ic.wlgitbridge.snapshot.base; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; - import java.util.Arrays; -import java.util.ArrayList; import java.util.List; +import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; public class MissingRepositoryException extends SnapshotAPIException { - public static final List GENERIC_REASON = Arrays.asList( - "This Overleaf project currently has no git access, either because", - "the project does not exist, or because git access is not enabled", - "for the project.", - "", - "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - ); - - static List buildDeprecatedMessage(String newUrl) { - if (newUrl == null) { - return Arrays.asList( - "This project has not yet been moved into the new version of Overleaf. You will", - "need to move it in order to continue working on it. Please visit this project", - "online on www.overleaf.com to do this.", - "", - "After migrating this project to the new version of Overleaf, you will be", - "prompted to update your git remote to the project's new identifier.", - "", - "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - ); - } else { - return Arrays.asList( - "This project has not yet been moved into the new version of Overleaf. You will", - "need to move it in order to continue working on it. Please visit this project", - "online to do this:", - "", - " " + newUrl, - "", - "After migrating this project to the new version of Overleaf, you will be", - "prompted to update your git remote to the project's new identifier.", - "", - "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - ); - } + public static final List GENERIC_REASON = + Arrays.asList( + "This Overleaf project currently has no git access, either because", + "the project does not exist, or because git access is not enabled", + "for the project.", + "", + "If this is unexpected, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information."); + static List buildDeprecatedMessage(String newUrl) { + if (newUrl == null) { + return Arrays.asList( + "This project has not yet been moved into the new version of Overleaf. You will", + "need to move it in order to continue working on it. Please visit this project", + "online on www.overleaf.com to do this.", + "", + "After migrating this project to the new version of Overleaf, you will be", + "prompted to update your git remote to the project's new identifier.", + "", + "If this is unexpected, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information."); + } else { + return Arrays.asList( + "This project has not yet been moved into the new version of Overleaf. You will", + "need to move it in order to continue working on it. Please visit this project", + "online to do this:", + "", + " " + newUrl, + "", + "After migrating this project to the new version of Overleaf, you will be", + "prompted to update your git remote to the project's new identifier.", + "", + "If this is unexpected, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information."); } + } - static List buildExportedToV2Message(String remoteUrl) { - if (remoteUrl == null) { - return Arrays.asList( - "This Overleaf project has been moved to Overleaf v2 and cannot be used with git at this time.", - "", - "If this error persists, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - ); - } else { - return Arrays.asList( - "This Overleaf project has been moved to Overleaf v2 and has a new identifier.", - "Please update your remote to:", - "", - " " + remoteUrl, - "", - "Assuming you are using the default \"origin\" remote, the following commands", - "will change the remote for you:", - "", - " git remote set-url origin " + remoteUrl, - "", - "If this does not work, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - ); - } + static List buildExportedToV2Message(String remoteUrl) { + if (remoteUrl == null) { + return Arrays.asList( + "This Overleaf project has been moved to Overleaf v2 and cannot be used with git at this time.", + "", + "If this error persists, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information."); + } else { + return Arrays.asList( + "This Overleaf project has been moved to Overleaf v2 and has a new identifier.", + "Please update your remote to:", + "", + " " + remoteUrl, + "", + "Assuming you are using the default \"origin\" remote, the following commands", + "will change the remote for you:", + "", + " git remote set-url origin " + remoteUrl, + "", + "If this does not work, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information."); } + } - private List descriptionLines; + private List descriptionLines; - public MissingRepositoryException() { this.descriptionLines = GENERIC_REASON; } + public MissingRepositoryException() { + this.descriptionLines = GENERIC_REASON; + } - public MissingRepositoryException(List descriptionLines) { - this.descriptionLines = descriptionLines; - } + public MissingRepositoryException(List descriptionLines) { + this.descriptionLines = descriptionLines; + } - @Override - public void fromJSON(JsonElement json) {} + @Override + public void fromJSON(JsonElement json) {} - @Override - public String getMessage() { - return "no git access"; - } - - @Override - public List getDescriptionLines() { - return this.descriptionLines; - } + @Override + public String getMessage() { + return "no git access"; + } + @Override + public List getDescriptionLines() { + return this.descriptionLines; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java index f44057a01b..4c5f31860a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java @@ -1,211 +1,197 @@ package uk.ac.ic.wlgitbridge.snapshot.base; +import static org.asynchttpclient.Dsl.*; + import com.google.api.client.http.*; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.*; +import javax.servlet.http.HttpServletResponse; import org.asynchttpclient.AsyncHttpClient; -import static org.asynchttpclient.Dsl.*; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Instance; import uk.ac.ic.wlgitbridge.util.Log; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.*; - -/** +/* * Created by Winston on 06/11/14. */ public abstract class Request { - public static final AsyncHttpClient httpClient = asyncHttpClient(); + public static final AsyncHttpClient httpClient = asyncHttpClient(); - private static final Executor executor = Executors.newCachedThreadPool(); + private static final Executor executor = Executors.newCachedThreadPool(); - private final String url; + private final String url; - private Future future; + private Future future; - public Request(String url) { - this.url = url; + public Request(String url) { + this.url = url; + } + + public CompletableFuture request() { + switch (httpMethod()) { + case GET: + performGetRequest(); + break; + case POST: + performPostRequest(); + break; + default: + break; } - - public CompletableFuture request() { - switch (httpMethod()) { - case GET: - performGetRequest(); - break; - case POST: - performPostRequest(); - break; - default: - break; - } - CompletableFuture ret = new CompletableFuture<>(); - executor.execute(() -> { - try { - ret.complete(getResult()); - } catch (Throwable t) { - ret.completeExceptionally(t); - } + CompletableFuture ret = new CompletableFuture<>(); + executor.execute( + () -> { + try { + ret.complete(getResult()); + } catch (Throwable t) { + ret.completeExceptionally(t); + } }); - return ret; - } + return ret; + } - private T getResult() throws MissingRepositoryException, FailedConnectionException, ForbiddenException { - try { - HttpResponse response = future.get(); - Log.debug( - "{} {} ({}B) -> " + url, - response.getStatusCode(), - response.getStatusMessage(), - response.getHeaders().getContentLength() - ); - JsonElement json = Instance.gson.fromJson( - response.parseAsString(), - JsonElement.class - ); - return parseResponse(json); - } catch (InterruptedException e) { - throw new FailedConnectionException(); - } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof HttpResponseException) { - HttpResponseException httpCause = (HttpResponseException) cause; - int sc = httpCause.getStatusCode(); - if (sc == HttpServletResponse.SC_UNAUTHORIZED || sc == HttpServletResponse.SC_FORBIDDEN) { // 401, 403 - throw new ForbiddenException(); - } else if (sc == 429) { // Too many requests - throw new MissingRepositoryException(Arrays.asList( - "Rate-limit exceeded. Please wait a while and try again.", - "", - "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - )); - } else if (sc == HttpServletResponse.SC_CONFLICT) { // 409 - try { - JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class); - String code = json.get("code").getAsString(); - if ("projectHasDotGit".equals(code)) { - throw new MissingRepositoryException(Arrays.asList( - "This project contains a '.git' entity at the top level, indicating that it is", - "already a git repository. The Overleaf git-bridge cannot work with this project", - "due to a known problem with handling these '.git' folders.", - "", - "We recommend removing the .git folder before trying again.", - "", - "If this is unexpected, please contact us at support@overleaf.com, or", - "see https://www.overleaf.com/learn/how-to/Git_integration for more information." - )); - } else { - throw new MissingRepositoryException(Arrays.asList("Conflict: 409")); - } - } catch (IllegalStateException - | ClassCastException - | NullPointerException _e) { // json parse errors - throw new MissingRepositoryException(Arrays.asList("Conflict: 409")); - } - } else if (sc == HttpServletResponse.SC_NOT_FOUND) { // 404 - try { - JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class); - String message = json.get("message").getAsString(); - String newRemote; - if (json.has("newRemote")) { - newRemote = json.get("newRemote").getAsString(); - } else { - newRemote = null; - } - - if ("Exported to v2".equals(message)) { - throw new MissingRepositoryException( - MissingRepositoryException.buildExportedToV2Message(newRemote) - ); - } else if ("Overleaf v1 is Deprecated".equals(message)) { - String newUrl; - if (json.has("newUrl")) { - newUrl = json.get("newUrl").getAsString(); - } else { - newUrl = null; - } - throw new MissingRepositoryException( - MissingRepositoryException.buildDeprecatedMessage(newUrl) - ); - } - } catch (IllegalStateException - | ClassCastException - | NullPointerException _) { - // disregard any errors that arose while handling the JSON - } - - throw new MissingRepositoryException(); - } else if (sc >= 400 && sc < 500) { - throw new MissingRepositoryException(MissingRepositoryException.GENERIC_REASON); - } - throw new FailedConnectionException(cause); + private T getResult() + throws MissingRepositoryException, FailedConnectionException, ForbiddenException { + try { + HttpResponse response = future.get(); + Log.debug( + "{} {} ({}B) -> " + url, + response.getStatusCode(), + response.getStatusMessage(), + response.getHeaders().getContentLength()); + JsonElement json = Instance.gson.fromJson(response.parseAsString(), JsonElement.class); + return parseResponse(json); + } catch (InterruptedException e) { + throw new FailedConnectionException(); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof HttpResponseException) { + HttpResponseException httpCause = (HttpResponseException) cause; + int sc = httpCause.getStatusCode(); + if (sc == HttpServletResponse.SC_UNAUTHORIZED + || sc == HttpServletResponse.SC_FORBIDDEN) { // 401, 403 + throw new ForbiddenException(); + } else if (sc == 429) { // Too many requests + throw new MissingRepositoryException( + Arrays.asList( + "Rate-limit exceeded. Please wait a while and try again.", + "", + "If this is unexpected, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information.")); + } else if (sc == HttpServletResponse.SC_CONFLICT) { // 409 + try { + JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class); + String code = json.get("code").getAsString(); + if ("projectHasDotGit".equals(code)) { + throw new MissingRepositoryException( + Arrays.asList( + "This project contains a '.git' entity at the top level, indicating that it is", + "already a git repository. The Overleaf git-bridge cannot work with this project", + "due to a known problem with handling these '.git' folders.", + "", + "We recommend removing the .git folder before trying again.", + "", + "If this is unexpected, please contact us at support@overleaf.com, or", + "see https://www.overleaf.com/learn/how-to/Git_integration for more information.")); } else { - throw new FailedConnectionException(cause); + throw new MissingRepositoryException(Arrays.asList("Conflict: 409")); } - } catch (IOException e) { - Log.warn("Failed to parse JSON.", e); - throw new FailedConnectionException(); + } catch (IllegalStateException + | ClassCastException + | NullPointerException _e) { // json parse errors + throw new MissingRepositoryException(Arrays.asList("Conflict: 409")); + } + } else if (sc == HttpServletResponse.SC_NOT_FOUND) { // 404 + try { + JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class); + String message = json.get("message").getAsString(); + String newRemote; + if (json.has("newRemote")) { + newRemote = json.get("newRemote").getAsString(); + } else { + newRemote = null; + } + + if ("Exported to v2".equals(message)) { + throw new MissingRepositoryException( + MissingRepositoryException.buildExportedToV2Message(newRemote)); + } else if ("Overleaf v1 is Deprecated".equals(message)) { + String newUrl; + if (json.has("newUrl")) { + newUrl = json.get("newUrl").getAsString(); + } else { + newUrl = null; + } + throw new MissingRepositoryException( + MissingRepositoryException.buildDeprecatedMessage(newUrl)); + } + } catch (IllegalStateException | ClassCastException | NullPointerException ex) { + // disregard any errors that arose while handling the JSON + } + + throw new MissingRepositoryException(); + } else if (sc >= 400 && sc < 500) { + throw new MissingRepositoryException(MissingRepositoryException.GENERIC_REASON); } + throw new FailedConnectionException(cause); + } else { + throw new FailedConnectionException(cause); + } + } catch (IOException e) { + Log.warn("Failed to parse JSON.", e); + throw new FailedConnectionException(); } + } - protected abstract HTTPMethod httpMethod(); + protected abstract HTTPMethod httpMethod(); - protected void onBeforeRequest(HttpRequest request) throws IOException { + protected void onBeforeRequest(HttpRequest request) throws IOException {} + protected abstract T parseResponse(JsonElement json) throws FailedConnectionException; + + protected String getPostBody() { + return null; + } + + private void performGetRequest() { + Log.debug("GET -> " + url); + try { + HttpRequest request = Instance.httpRequestFactory.buildGetRequest(new GenericUrl(url)); + setTimeouts(request); + request(request); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); } + } - protected abstract - T parseResponse(JsonElement json) throws FailedConnectionException; - - protected String getPostBody() { - return null; + private void performPostRequest() { + Log.debug("POST -> " + url); + try { + HttpRequest request = + Instance.httpRequestFactory.buildPostRequest( + new GenericUrl(url), + new ByteArrayContent("application/json", getPostBody().getBytes())); + setTimeouts(request); + request(request); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); } + } - private void performGetRequest() { - Log.debug("GET -> " + url); - try { - HttpRequest request = Instance.httpRequestFactory.buildGetRequest( - new GenericUrl(url) - ); - setTimeouts(request); - request(request); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - private void performPostRequest() { - Log.debug("POST -> " + url); - try { - HttpRequest request = Instance.httpRequestFactory.buildPostRequest( - new GenericUrl(url), - new ByteArrayContent( - "application/json", - getPostBody().getBytes() - ) - ); - setTimeouts(request); - request(request); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - private void request(HttpRequest request) throws IOException { - onBeforeRequest(request); - future = request.executeAsync(); - } - - private void setTimeouts(HttpRequest request) { - // timeouts are 20s by default - int threeMinutesInMs = 1000 * 60 * 3; - request.setReadTimeout(threeMinutesInMs); - } + private void request(HttpRequest request) throws IOException { + onBeforeRequest(request); + future = request.executeAsync(); + } + private void setTimeouts(HttpRequest request) { + // timeouts are 20s by default + int threeMinutesInMs = 1000 * 60 * 3; + request.setReadTimeout(threeMinutesInMs); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Result.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Result.java index 05dc4c8af4..2c4adb3866 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Result.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Result.java @@ -2,35 +2,33 @@ package uk.ac.ic.wlgitbridge.snapshot.base; import com.google.gson.JsonElement; -/** +/* * Created by Winston on 06/11/14. */ public abstract class Result implements JSONSource { - private JsonElement json; - private Request request; + private JsonElement json; + private Request request; - public Result(Request request, JsonElement json) { - this.request = request; - this.json = json; - fromJSON(json); + public Result(Request request, JsonElement json) { + this.request = request; + this.json = json; + fromJSON(json); + } + + protected Result() {} + + public Request getRequest() { + return request; + } + + public abstract JsonElement toJson(); + + @Override + public String toString() { + if (json == null) { + return "result"; } - - protected Result() { - } - - public Request getRequest() { - return request; - } - - public abstract JsonElement toJson(); - - @Override - public String toString() { - if (json == null) { - return "result"; - } - return json.toString(); - } - + return json.toString(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/SnapshotAPIRequest.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/SnapshotAPIRequest.java index 8d318653c2..7fc3b8472f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/SnapshotAPIRequest.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/SnapshotAPIRequest.java @@ -1,43 +1,34 @@ package uk.ac.ic.wlgitbridge.snapshot.base; import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.http.BasicAuthentication; import com.google.api.client.http.HttpRequest; -import java.io.IOException; - -/** +/* * Created by Winston on 06/11/14. */ public abstract class SnapshotAPIRequest extends Request { - private static String BASE_URL; + private static String BASE_URL; - private final Credential oauth2; + private final Credential oauth2; - public SnapshotAPIRequest( - String projectName, - String apiCall, - Credential oauth2 - ) { - super(BASE_URL + projectName + apiCall); - this.oauth2 = oauth2; - } - - @Override - protected void onBeforeRequest( - HttpRequest request - ) { - if (oauth2 != null) { - request.setInterceptor(request1 -> { - oauth2.intercept(request1); - }); - } - } - - /* baseURL ends with / */ - public static void setBaseURL(String baseURL) { - BASE_URL = baseURL + "docs/"; + public SnapshotAPIRequest(String projectName, String apiCall, Credential oauth2) { + super(BASE_URL + projectName + apiCall); + this.oauth2 = oauth2; + } + + @Override + protected void onBeforeRequest(HttpRequest request) { + if (oauth2 != null) { + request.setInterceptor( + request1 -> { + oauth2.intercept(request1); + }); } + } + /* baseURL ends with / */ + public static void setBaseURL(String baseURL) { + BASE_URL = baseURL + "docs/"; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/exception/FailedConnectionException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/exception/FailedConnectionException.java index c81b8abade..ee3187fdd1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/exception/FailedConnectionException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/exception/FailedConnectionException.java @@ -3,18 +3,16 @@ package uk.ac.ic.wlgitbridge.snapshot.exception; import org.eclipse.jgit.transport.ServiceMayNotContinueException; import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 08/11/14. */ public class FailedConnectionException extends ServiceMayNotContinueException { - public FailedConnectionException() { - super(Util.getServiceName() + - " server not available. Please try again later."); - } - - public FailedConnectionException(Throwable cause) { - super(cause); - } + public FailedConnectionException() { + super(Util.getServiceName() + " server not available. Please try again later."); + } + public FailedConnectionException(Throwable cause) { + super(cause); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocRequest.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocRequest.java index 66900d3b5a..94189a439e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocRequest.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocRequest.java @@ -1,45 +1,35 @@ package uk.ac.ic.wlgitbridge.snapshot.getdoc; import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.http.HttpRequest; import com.google.gson.JsonElement; import uk.ac.ic.wlgitbridge.snapshot.base.HTTPMethod; import uk.ac.ic.wlgitbridge.snapshot.base.SnapshotAPIRequest; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Log; -import java.io.IOException; - -/** +/* * Created by Winston on 06/11/14. */ public class GetDocRequest extends SnapshotAPIRequest { - public static final String API_CALL = ""; + public static final String API_CALL = ""; - public GetDocRequest(Credential oauth2, String projectName) { - super(projectName, API_CALL, oauth2); - Log.debug( - "GetDocRequest({}, {})", - "oauth2: ", - "projectName: " + projectName - ); - } + public GetDocRequest(Credential oauth2, String projectName) { + super(projectName, API_CALL, oauth2); + Log.debug("GetDocRequest({}, {})", "oauth2: ", "projectName: " + projectName); + } - public GetDocRequest(String projectName) { - this(null, projectName); - } + public GetDocRequest(String projectName) { + this(null, projectName); + } - @Override - protected HTTPMethod httpMethod() { - return HTTPMethod.GET; - } - - @Override - protected GetDocResult parseResponse( - JsonElement json - ) throws FailedConnectionException { - return new GetDocResult(this, json); - } + @Override + protected HTTPMethod httpMethod() { + return HTTPMethod.GET; + } + @Override + protected GetDocResult parseResponse(JsonElement json) throws FailedConnectionException { + return new GetDocResult(this, json); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocResult.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocResult.java index ac93c08ca8..88c10bacee 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocResult.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/GetDocResult.java @@ -12,125 +12,114 @@ import uk.ac.ic.wlgitbridge.snapshot.getdoc.exception.InvalidProjectException; import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.WLUser; import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 06/11/14. */ public class GetDocResult extends Result { - private int error; - private int versionID; - private String createdAt; - private WLUser user; + private int error; + private int versionID; + private String createdAt; + private WLUser user; - private SnapshotAPIException exception; - private ForbiddenException forbidden; + private SnapshotAPIException exception; + private ForbiddenException forbidden; - public GetDocResult( - Request request, - JsonElement json - ) throws FailedConnectionException { - super(request, json); + public GetDocResult(Request request, JsonElement json) throws FailedConnectionException { + super(request, json); + } + + public GetDocResult( + JsonElement error, int versionID, String createdAt, String email, String name) { + if (error == null) { + this.error = -1; + } else { + this.error = error.getAsInt(); } + this.versionID = versionID; + this.createdAt = createdAt; + this.user = new WLUser(name, email); + } - public GetDocResult( - JsonElement error, - int versionID, - String createdAt, - String email, - String name - ) { - if (error == null) { - this.error = -1; - } else { - this.error = error.getAsInt(); - } - this.versionID = versionID; - this.createdAt = createdAt; - this.user = new WLUser(name, email); + @Override + public JsonElement toJson() { + JsonObject jsonThis = new JsonObject(); + if (error == -1) { + jsonThis.addProperty("latestVerId", versionID); + jsonThis.addProperty("latestVerAt", createdAt); + JsonObject latestVerBy = new JsonObject(); + latestVerBy.addProperty("email", getEmail()); + latestVerBy.addProperty("name", getName()); + jsonThis.add("latestVerBy", latestVerBy); + } else { + jsonThis.addProperty("status", error); + String message; + if (error == 403) { + message = "Forbidden"; + } else { + message = "Not Found"; + } + jsonThis.addProperty("message", message); } + return jsonThis; + } - @Override - public JsonElement toJson() { - JsonObject jsonThis = new JsonObject(); - if (error == -1) { - jsonThis.addProperty("latestVerId", versionID); - jsonThis.addProperty("latestVerAt", createdAt); - JsonObject latestVerBy = new JsonObject(); - latestVerBy.addProperty("email", getEmail()); - latestVerBy.addProperty("name", getName()); - jsonThis.add("latestVerBy", latestVerBy); - } else { - jsonThis.addProperty("status", error); - String message; - if (error == 403) { - message = "Forbidden"; - } else { - message = "Not Found"; - } - jsonThis.addProperty("message", message); - } - return jsonThis; + @Override + public void fromJSON(JsonElement json) { + Log.debug("GetDocResult: " + json); + JsonObject jsonObject = json.getAsJsonObject(); + if (jsonObject.has("status")) { + switch (jsonObject.get("status").getAsInt()) { + case 401: + case 403: + forbidden = new ForbiddenException(); + break; + case 404: + exception = new InvalidProjectException(); + break; + default: + throw new IllegalArgumentException("unknown get doc error code"); + } + } else { + versionID = jsonObject.get("latestVerId").getAsInt(); + // Handle edge-case for projects with no changes, that were imported + // to v2. In which case `latestVerAt` will not be present. + // See: https://github.com/overleaf/writelatex-git-bridge/pull/50 + if (jsonObject.has("latestVerAt")) { + createdAt = jsonObject.get("latestVerAt").getAsString(); + } else { + createdAt = null; + } + String name = null; + String email = null; + JsonElement latestVerBy = jsonObject.get("latestVerBy"); + + if (latestVerBy.isJsonObject()) { + JsonObject userObject = latestVerBy.getAsJsonObject(); + name = userObject.get("name").getAsString(); + email = userObject.get("email").getAsString(); + } + + user = new WLUser(name, email); } + } - @Override - public void fromJSON(JsonElement json) { - Log.debug("GetDocResult: " + json); - JsonObject jsonObject = json.getAsJsonObject(); - if (jsonObject.has("status")) { - switch (jsonObject.get("status").getAsInt()) { - case 401: - case 403: - forbidden = new ForbiddenException(); - break; - case 404: - exception = new InvalidProjectException(); - break; - default: - throw new IllegalArgumentException( - "unknown get doc error code" - ); - } - } else { - versionID = jsonObject.get("latestVerId").getAsInt(); - // Handle edge-case for projects with no changes, that were imported - // to v2. In which case `latestVerAt` will not be present. - // See: https://github.com/overleaf/writelatex-git-bridge/pull/50 - if (jsonObject.has("latestVerAt")) { - createdAt = jsonObject.get("latestVerAt").getAsString(); - } else { - createdAt = null; - } - String name = null; - String email = null; - JsonElement latestVerBy = jsonObject.get("latestVerBy"); - - if (latestVerBy.isJsonObject()) { - JsonObject userObject = latestVerBy.getAsJsonObject(); - name = userObject.get("name").getAsString(); - email = userObject.get("email").getAsString(); - } - - user = new WLUser(name, email); - } + public int getVersionID() throws GitUserException { + if (exception != null) { + throw exception; } + return versionID; + } - public int getVersionID() throws GitUserException { - if (exception != null) { - throw exception; - } - return versionID; - } + public String getCreatedAt() { + return createdAt; + } - public String getCreatedAt() { - return createdAt; - } - - public String getName() { - return user.getName(); - } - - public String getEmail() { - return user.getEmail(); - } + public String getName() { + return user.getName(); + } + public String getEmail() { + return user.getEmail(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/exception/InvalidProjectException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/exception/InvalidProjectException.java index a9d4bd1c6e..86b9e20dcc 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/exception/InvalidProjectException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getdoc/exception/InvalidProjectException.java @@ -2,40 +2,38 @@ package uk.ac.ic.wlgitbridge.snapshot.getdoc.exception; import com.google.gson.JsonArray; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; - import java.util.LinkedList; import java.util.List; +import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; -/** +/* * Created by Winston on 08/11/14. */ public class InvalidProjectException extends SnapshotAPIException { - private List errors; + private List errors; - public InvalidProjectException() { - super(); - errors = new LinkedList(); - } + public InvalidProjectException() { + super(); + errors = new LinkedList(); + } - @Override - public String getMessage() { - return "invalid project"; - } + @Override + public String getMessage() { + return "invalid project"; + } - @Override - public List getDescriptionLines() { - return errors; - } + @Override + public List getDescriptionLines() { + return errors; + } - @Override - public void fromJSON(JsonElement json) { - errors = new LinkedList(); - JsonArray errors = - json.getAsJsonObject().get("errors").getAsJsonArray(); - for (JsonElement error : errors) { - this.errors.add(error.getAsString()); - } + @Override + public void fromJSON(JsonElement json) { + errors = new LinkedList(); + JsonArray errors = json.getAsJsonObject().get("errors").getAsJsonArray(); + for (JsonElement error : errors) { + this.errors.add(error.getAsString()); } + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionRequest.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionRequest.java index 2246f6f95a..ca69f2e6b3 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionRequest.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionRequest.java @@ -7,45 +7,36 @@ import uk.ac.ic.wlgitbridge.snapshot.base.SnapshotAPIRequest; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 06/11/14. */ -public class GetForVersionRequest - extends SnapshotAPIRequest { +public class GetForVersionRequest extends SnapshotAPIRequest { - public static final String API_CALL = "/snapshots"; + public static final String API_CALL = "/snapshots"; - private int versionID; + private int versionID; - public GetForVersionRequest( - Credential oauth2, - String projectName, - int versionID - ) { - super(projectName, API_CALL + "/" + versionID, oauth2); - this.versionID = versionID; - Log.debug( - "GetForVersionRequest({}, {}, {})", - "oauth2: ", - "projectName: " + projectName, - "versionID: " + versionID - ); - } + public GetForVersionRequest(Credential oauth2, String projectName, int versionID) { + super(projectName, API_CALL + "/" + versionID, oauth2); + this.versionID = versionID; + Log.debug( + "GetForVersionRequest({}, {}, {})", + "oauth2: ", + "projectName: " + projectName, + "versionID: " + versionID); + } - @Override - protected HTTPMethod httpMethod() { - return HTTPMethod.GET; - } + @Override + protected HTTPMethod httpMethod() { + return HTTPMethod.GET; + } - @Override - protected GetForVersionResult parseResponse( - JsonElement json - ) throws FailedConnectionException { - return new GetForVersionResult(this, json); - } - - public int getVersionID() { - return versionID; - } + @Override + protected GetForVersionResult parseResponse(JsonElement json) throws FailedConnectionException { + return new GetForVersionResult(this, json); + } + public int getVersionID() { + return versionID; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionResult.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionResult.java index 3ff2e3ac9a..63bf5ebf7c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionResult.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/GetForVersionResult.java @@ -5,34 +5,33 @@ import uk.ac.ic.wlgitbridge.snapshot.base.Request; import uk.ac.ic.wlgitbridge.snapshot.base.Result; import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 06/11/14. */ public class GetForVersionResult extends Result { - private SnapshotData snapshotData; + private SnapshotData snapshotData; - public GetForVersionResult(Request request, JsonElement json) { - super(request, json); - } + public GetForVersionResult(Request request, JsonElement json) { + super(request, json); + } - public GetForVersionResult(SnapshotData snapshotData) { - this.snapshotData = snapshotData; - } + public GetForVersionResult(SnapshotData snapshotData) { + this.snapshotData = snapshotData; + } - @Override - public JsonElement toJson() { - return snapshotData.toJson(); - } + @Override + public JsonElement toJson() { + return snapshotData.toJson(); + } - @Override - public void fromJSON(JsonElement json) { - snapshotData = new SnapshotData(json); - Log.debug("GetForVersionResult({})", snapshotData); - } - - public SnapshotData getSnapshotData() { - return snapshotData; - } + @Override + public void fromJSON(JsonElement json) { + snapshotData = new SnapshotData(json); + Log.debug("GetForVersionResult({})", snapshotData); + } + public SnapshotData getSnapshotData() { + return snapshotData; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotAttachment.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotAttachment.java index bc65c4171c..cb897d50c6 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotAttachment.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotAttachment.java @@ -5,50 +5,49 @@ import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; -/** +/* * Created by Winston on 06/11/14. */ public class SnapshotAttachment implements JSONSource { - private String url; - private String path; + private String url; + private String path; - public SnapshotAttachment(JsonElement json) { - fromJSON(json); - } + public SnapshotAttachment(JsonElement json) { + fromJSON(json); + } - @Override - public String toString() { - return "SnapshotAttachment(url: " + url + ", path: " + path + ")"; - } + @Override + public String toString() { + return "SnapshotAttachment(url: " + url + ", path: " + path + ")"; + } - @Override - public void fromJSON(JsonElement json) { - JsonArray jsonArray = json.getAsJsonArray(); - url = jsonArray.get(0).getAsString(); - path = jsonArray.get(1).getAsString(); - } + @Override + public void fromJSON(JsonElement json) { + JsonArray jsonArray = json.getAsJsonArray(); + url = jsonArray.get(0).getAsString(); + path = jsonArray.get(1).getAsString(); + } - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public String getPath() { - return path; - } + public String getPath() { + return path; + } - /* For the Mock Snapshot server */ + /* For the Mock Snapshot server */ - public SnapshotAttachment(String url, String path) { - this.url = url; - this.path = path; - } - - public JsonElement toJson() { - JsonArray jsonThis = new JsonArray(); - jsonThis.add(new JsonPrimitive(url)); - jsonThis.add(new JsonPrimitive(getPath())); - return jsonThis; - } + public SnapshotAttachment(String url, String path) { + this.url = url; + this.path = path; + } + public JsonElement toJson() { + JsonArray jsonThis = new JsonArray(); + jsonThis.add(new JsonPrimitive(url)); + jsonThis.add(new JsonPrimitive(getPath())); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotData.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotData.java index a382f16d09..bad0524871 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotData.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotData.java @@ -3,84 +3,75 @@ package uk.ac.ic.wlgitbridge.snapshot.getforversion; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; - import java.util.ArrayList; import java.util.List; +import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; -/** +/* * Created by Winston on 06/11/14. */ public class SnapshotData implements JSONSource { - public static final String JSON_KEY_SRCS = "srcs"; - public static final String JSON_KEY_ATTS = "atts"; + public static final String JSON_KEY_SRCS = "srcs"; + public static final String JSON_KEY_ATTS = "atts"; - private List srcs; - private List atts; + private List srcs; + private List atts; - public SnapshotData(JsonElement json) { - srcs = new ArrayList<>(); - atts = new ArrayList<>(); - fromJSON(json); + public SnapshotData(JsonElement json) { + srcs = new ArrayList<>(); + atts = new ArrayList<>(); + fromJSON(json); + } + + public SnapshotData(List srcs, List atts) { + this.srcs = srcs; + this.atts = atts; + } + + @Override + public String toString() { + return "SnapshotData(srcs: " + srcs + ", atts: " + atts + ")"; + } + + public JsonElement toJson() { + JsonObject jsonThis = new JsonObject(); + JsonArray jsonSrcs = new JsonArray(); + for (SnapshotFile src : srcs) { + jsonSrcs.add(src.toJson()); } - - public SnapshotData( - List srcs, - List atts - ) { - this.srcs = srcs; - this.atts = atts; + jsonThis.add("srcs", jsonSrcs); + JsonArray jsonAtts = new JsonArray(); + for (SnapshotAttachment att : atts) { + jsonAtts.add(att.toJson()); } + jsonThis.add("atts", jsonAtts); + return jsonThis; + } - @Override - public String toString() { - return "SnapshotData(srcs: " + srcs + ", atts: " + atts + ")"; + @Override + public void fromJSON(JsonElement json) { + populateSrcs(json.getAsJsonObject().get(JSON_KEY_SRCS).getAsJsonArray()); + populateAtts(json.getAsJsonObject().get(JSON_KEY_ATTS).getAsJsonArray()); + } + + private void populateSrcs(JsonArray jsonArray) { + for (JsonElement json : jsonArray) { + srcs.add(new SnapshotFile(json)); } + } - public JsonElement toJson() { - JsonObject jsonThis = new JsonObject(); - JsonArray jsonSrcs = new JsonArray(); - for (SnapshotFile src : srcs) { - jsonSrcs.add(src.toJson()); - } - jsonThis.add("srcs", jsonSrcs); - JsonArray jsonAtts = new JsonArray(); - for (SnapshotAttachment att : atts) { - jsonAtts.add(att.toJson()); - } - jsonThis.add("atts", jsonAtts); - return jsonThis; + private void populateAtts(JsonArray jsonArray) { + for (JsonElement json : jsonArray) { + atts.add(new SnapshotAttachment(json)); } + } - @Override - public void fromJSON(JsonElement json) { - populateSrcs( - json.getAsJsonObject().get(JSON_KEY_SRCS).getAsJsonArray() - ); - populateAtts( - json.getAsJsonObject().get(JSON_KEY_ATTS).getAsJsonArray() - ); - } - - private void populateSrcs(JsonArray jsonArray) { - for (JsonElement json : jsonArray) { - srcs.add(new SnapshotFile(json)); - } - } - - private void populateAtts(JsonArray jsonArray) { - for (JsonElement json : jsonArray) { - atts.add(new SnapshotAttachment(json)); - } - } - - public List getSrcs() { - return srcs; - } - - public List getAtts() { - return atts; - } + public List getSrcs() { + return srcs; + } + public List getAtts() { + return atts; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotFile.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotFile.java index a74ec53eb3..e7f55f8037 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotFile.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getforversion/SnapshotFile.java @@ -6,65 +6,60 @@ import com.google.gson.JsonPrimitive; import uk.ac.ic.wlgitbridge.data.filestore.RawFile; import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource; -/** +/* * Created by Winston on 06/11/14. */ public class SnapshotFile extends RawFile implements JSONSource { - private String path; - private byte[] contents; + private String path; + private byte[] contents; - public SnapshotFile(JsonElement json) { - fromJSON(json); - } - - @Override - public String toString() { - return "SnapshotFile(path: " - + path - + ", contents: byte[" - + contents.length - + "])"; - } - - @Override - public void fromJSON(JsonElement json) { - JsonArray jsonArray = json.getAsJsonArray(); - contents = jsonArray.get(0).getAsString().getBytes(); - path = jsonArray.get(1).getAsString(); - } - - @Override - public String getPath() { - return path; - } - - @Override - public byte[] getContents() { - return contents; - } - - @Override - public long size() { - return contents.length; - } - - /* Mock server */ - - public SnapshotFile(String contents, String path) { - this.path = path; - if (contents != null) { - this.contents = contents.getBytes(); - } else { - this.contents = new byte[0]; - } - } - - public JsonElement toJson() { - JsonArray jsonThis = new JsonArray(); - jsonThis.add(new JsonPrimitive(new String(contents))); - jsonThis.add(new JsonPrimitive(path)); - return jsonThis; + public SnapshotFile(JsonElement json) { + fromJSON(json); + } + + @Override + public String toString() { + return "SnapshotFile(path: " + path + ", contents: byte[" + contents.length + "])"; + } + + @Override + public void fromJSON(JsonElement json) { + JsonArray jsonArray = json.getAsJsonArray(); + contents = jsonArray.get(0).getAsString().getBytes(); + path = jsonArray.get(1).getAsString(); + } + + @Override + public String getPath() { + return path; + } + + @Override + public byte[] getContents() { + return contents; + } + + @Override + public long size() { + return contents.length; + } + + /* Mock server */ + + public SnapshotFile(String contents, String path) { + this.path = path; + if (contents != null) { + this.contents = contents.getBytes(); + } else { + this.contents = new byte[0]; } + } + public JsonElement toJson() { + JsonArray jsonThis = new JsonArray(); + jsonThis.add(new JsonPrimitive(new String(contents))); + jsonThis.add(new JsonPrimitive(path)); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersRequest.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersRequest.java index 10ac6e4cd4..d2d07aa2bc 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersRequest.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersRequest.java @@ -2,38 +2,30 @@ package uk.ac.ic.wlgitbridge.snapshot.getsavedvers; import com.google.api.client.auth.oauth2.Credential; import com.google.gson.JsonElement; +import uk.ac.ic.wlgitbridge.snapshot.base.HTTPMethod; import uk.ac.ic.wlgitbridge.snapshot.base.SnapshotAPIRequest; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; -import uk.ac.ic.wlgitbridge.snapshot.base.HTTPMethod; import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 06/11/14. */ -public class GetSavedVersRequest - extends SnapshotAPIRequest { +public class GetSavedVersRequest extends SnapshotAPIRequest { - public static final String API_CALL = "/saved_vers"; + public static final String API_CALL = "/saved_vers"; - public GetSavedVersRequest(Credential oauth2, String projectName) { - super(projectName, API_CALL, oauth2); - Log.debug( - "GetSavedVersRequest({}, {})", - "oauth2: ", - "projectName: " + projectName - ); - } + public GetSavedVersRequest(Credential oauth2, String projectName) { + super(projectName, API_CALL, oauth2); + Log.debug("GetSavedVersRequest({}, {})", "oauth2: ", "projectName: " + projectName); + } - @Override - protected HTTPMethod httpMethod() { - return HTTPMethod.GET; - } - - @Override - protected GetSavedVersResult parseResponse( - JsonElement json - ) throws FailedConnectionException { - return new GetSavedVersResult(this, json); - } + @Override + protected HTTPMethod httpMethod() { + return HTTPMethod.GET; + } + @Override + protected GetSavedVersResult parseResponse(JsonElement json) throws FailedConnectionException { + return new GetSavedVersResult(this, json); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersResult.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersResult.java index 5e5fe91735..0710061e7b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersResult.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/GetSavedVersResult.java @@ -4,66 +4,56 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.List; import uk.ac.ic.wlgitbridge.snapshot.base.Request; import uk.ac.ic.wlgitbridge.snapshot.base.Result; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Log; -import java.util.ArrayList; -import java.util.List; - -/** +/* * Created by Winston on 06/11/14. */ public class GetSavedVersResult extends Result { - private List savedVers; + private List savedVers; - public GetSavedVersResult( - Request request, - JsonElement json - ) throws FailedConnectionException { - super(request, json); + public GetSavedVersResult(Request request, JsonElement json) throws FailedConnectionException { + super(request, json); + } + + public GetSavedVersResult(List savedVers) { + this.savedVers = savedVers; + } + + @Override + public JsonElement toJson() { + JsonArray jsonThis = new JsonArray(); + for (SnapshotInfo savedVer : savedVers) { + JsonObject jsonSavedVer = new JsonObject(); + jsonSavedVer.addProperty("versionId", savedVer.getVersionId()); + jsonSavedVer.addProperty("comment", savedVer.getComment()); + WLUser user = savedVer.getUser(); + JsonObject jsonUser = new JsonObject(); + jsonUser.addProperty("email", user.getEmail()); + jsonUser.addProperty("name", user.getName()); + jsonSavedVer.add("user", jsonUser); + jsonSavedVer.addProperty("createdAt", savedVer.getCreatedAt()); + jsonThis.add(jsonSavedVer); } + return jsonThis; + } - public GetSavedVersResult(List savedVers) { - this.savedVers = savedVers; - } - - @Override - public JsonElement toJson() { - JsonArray jsonThis = new JsonArray(); - for (SnapshotInfo savedVer : savedVers) { - JsonObject jsonSavedVer = new JsonObject(); - jsonSavedVer.addProperty("versionId", savedVer.getVersionId()); - jsonSavedVer.addProperty("comment", savedVer.getComment()); - WLUser user = savedVer.getUser(); - JsonObject jsonUser = new JsonObject(); - jsonUser.addProperty("email", user.getEmail()); - jsonUser.addProperty("name", user.getName()); - jsonSavedVer.add("user", jsonUser); - jsonSavedVer.addProperty("createdAt", savedVer.getCreatedAt()); - jsonThis.add(jsonSavedVer); - } - return jsonThis; - } - - @Override - public void fromJSON(JsonElement json) { - Log.debug("GetSavedVersResult({})", json); - savedVers = new ArrayList<>(); - for (JsonElement elem : json.getAsJsonArray()) { - savedVers.add( - new Gson().fromJson( - elem.getAsJsonObject(), - SnapshotInfo.class - ) - ); - } - } - - public List getSavedVers() { - return savedVers; + @Override + public void fromJSON(JsonElement json) { + Log.debug("GetSavedVersResult({})", json); + savedVers = new ArrayList<>(); + for (JsonElement elem : json.getAsJsonArray()) { + savedVers.add(new Gson().fromJson(elem.getAsJsonObject(), SnapshotInfo.class)); } + } + public List getSavedVers() { + return savedVers; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/SnapshotInfo.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/SnapshotInfo.java index 3fe1a2e36c..8818aa86d1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/SnapshotInfo.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/SnapshotInfo.java @@ -2,72 +2,54 @@ package uk.ac.ic.wlgitbridge.snapshot.getsavedvers; import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 06/11/14. */ public class SnapshotInfo implements Comparable { - private int versionId; - private String comment; - private WLUser user; - private String createdAt; + private int versionId; + private String comment; + private WLUser user; + private String createdAt; - public SnapshotInfo( - int versionID, - String createdAt, - String name, - String email - ) { - this( - versionID, - "Update on " + Util.getServiceName() + ".", - email, - name, - createdAt - ); - } - - public SnapshotInfo( - int versionID, - String comment, - String email, - String name, - String createdAt - ) { - versionId = versionID; - this.comment = comment; - user = new WLUser(name, email); - this.createdAt = createdAt; - } - - public int getVersionId() { - return versionId; - } - - public String getComment() { - return comment; - } - - public WLUser getUser() { - return user != null ? user : new WLUser(); - } - - public String getCreatedAt() { - return createdAt; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof SnapshotInfo)) { - return false; - } - SnapshotInfo that = (SnapshotInfo) obj; - return versionId == that.versionId; - } - - @Override - public int compareTo(SnapshotInfo o) { - return Integer.compare(versionId, o.versionId); + public SnapshotInfo(int versionID, String createdAt, String name, String email) { + this(versionID, "Update on " + Util.getServiceName() + ".", email, name, createdAt); + } + + public SnapshotInfo(int versionID, String comment, String email, String name, String createdAt) { + versionId = versionID; + this.comment = comment; + user = new WLUser(name, email); + this.createdAt = createdAt; + } + + public int getVersionId() { + return versionId; + } + + public String getComment() { + return comment; + } + + public WLUser getUser() { + return user != null ? user : new WLUser(); + } + + public String getCreatedAt() { + return createdAt; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SnapshotInfo)) { + return false; } + SnapshotInfo that = (SnapshotInfo) obj; + return versionId == that.versionId; + } + @Override + public int compareTo(SnapshotInfo o) { + return Integer.compare(versionId, o.versionId); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/WLUser.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/WLUser.java index 3ff2742416..61867f92ab 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/WLUser.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/getsavedvers/WLUser.java @@ -2,40 +2,38 @@ package uk.ac.ic.wlgitbridge.snapshot.getsavedvers; import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 06/11/14. */ public class WLUser { - private final String name; - private final String email; + private final String name; + private final String email; - public WLUser() { - this(null, null); - } + public WLUser() { + this(null, null); + } - public WLUser(String name, String email) { - if (name != null && email != null) { - this.name = name; - this.email = email; - } else { - this.name = "Anonymous"; - this.email = "anonymous@" - + Util.getServiceName().toLowerCase() - + ".com"; - } + public WLUser(String name, String email) { + if (name != null && email != null) { + this.name = name; + this.email = email; + } else { + this.name = "Anonymous"; + this.email = "anonymous@" + Util.getServiceName().toLowerCase() + ".com"; } + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getEmail() { - return email; - } + public String getEmail() { + return email; + } - @Override - public String toString() { - return "(" + name + ", " + email + ")"; - } + @Override + public String toString() { + return "(" + name + ", " + email + ")"; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManager.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManager.java index b16427b5cf..2b15b61c1e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManager.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManager.java @@ -1,97 +1,81 @@ package uk.ac.ic.wlgitbridge.snapshot.push; import com.google.common.base.Preconditions; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.InvalidPostbackKeyException; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException; - import java.math.BigInteger; import java.security.SecureRandom; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.InvalidPostbackKeyException; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException; -/** +/* * Created by Winston on 17/11/14. */ public class PostbackManager { - private final SecureRandom random; - final Map postbackContentsTable; + private final SecureRandom random; + final Map postbackContentsTable; - PostbackManager(SecureRandom random) { - this.random = random; - postbackContentsTable = Collections.synchronizedMap( - new HashMap() - ); + PostbackManager(SecureRandom random) { + this.random = random; + postbackContentsTable = Collections.synchronizedMap(new HashMap()); + } + + public PostbackManager() { + this(new SecureRandom()); + } + + public int waitForVersionIdOrThrow(String projectName) throws SnapshotPostException { + try { + PostbackPromise postbackPromise = postbackContentsTable.get(projectName); + Preconditions.checkNotNull(postbackPromise); + return postbackPromise.waitForPostback(); + } finally { + postbackContentsTable.remove(projectName); } + } - public PostbackManager() { - this(new SecureRandom()); + public void postVersionIDForProject(String projectName, int versionID, String postbackKey) + throws UnexpectedPostbackException { + getPostbackForProject(projectName).receivedVersionID(versionID, postbackKey); + } + + public void postExceptionForProject( + String projectName, SnapshotPostException exception, String postbackKey) + throws UnexpectedPostbackException { + getPostbackForProject(projectName).receivedException(exception, postbackKey); + } + + private PostbackPromise getPostbackForProject(String projectName) + throws UnexpectedPostbackException { + PostbackPromise contents = postbackContentsTable.get(projectName); + if (contents == null) { + throw new UnexpectedPostbackException(); } + return contents; + } - public int waitForVersionIdOrThrow( - String projectName - ) throws SnapshotPostException { - try { - PostbackPromise postbackPromise = - postbackContentsTable.get(projectName); - Preconditions.checkNotNull(postbackPromise); - return postbackPromise.waitForPostback(); - } finally { - postbackContentsTable.remove(projectName); - } - } - - public void postVersionIDForProject( - String projectName, - int versionID, - String postbackKey - ) throws UnexpectedPostbackException { - getPostbackForProject( - projectName - ).receivedVersionID(versionID, postbackKey); - } - - public void postExceptionForProject( - String projectName, - SnapshotPostException exception, - String postbackKey - ) throws UnexpectedPostbackException { - getPostbackForProject( - projectName - ).receivedException(exception, postbackKey); - } - - private PostbackPromise getPostbackForProject(String projectName) - throws UnexpectedPostbackException { - PostbackPromise contents = postbackContentsTable.get(projectName); - if (contents == null) { - throw new UnexpectedPostbackException(); - } - return contents; - } - - public String makeKeyForProject(String projectName) { - String key = System.currentTimeMillis() + randomString(); - PostbackPromise contents = new PostbackPromise(key); - postbackContentsTable.put(projectName, contents); - return key; - } - - public void checkPostbackKey(String projectName, String postbackKey) - throws InvalidPostbackKeyException { - PostbackPromise postbackPromise = postbackContentsTable.get(projectName); - if (postbackPromise == null) { - // project not found; can't check key - throw new InvalidPostbackKeyException(); - } else { - postbackPromise.checkPostbackKey(postbackKey); - } - } - - private String randomString() { - return new BigInteger(130, random).toString(32); + public String makeKeyForProject(String projectName) { + String key = System.currentTimeMillis() + randomString(); + PostbackPromise contents = new PostbackPromise(key); + postbackContentsTable.put(projectName, contents); + return key; + } + + public void checkPostbackKey(String projectName, String postbackKey) + throws InvalidPostbackKeyException { + PostbackPromise postbackPromise = postbackContentsTable.get(projectName); + if (postbackPromise == null) { + // project not found; can't check key + throw new InvalidPostbackKeyException(); + } else { + postbackPromise.checkPostbackKey(postbackKey); } + } + private String randomString() { + return new BigInteger(130, random).toString(32); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackPromise.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackPromise.java index 24587a5c5c..f8b55d32c1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackPromise.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackPromise.java @@ -1,93 +1,86 @@ package uk.ac.ic.wlgitbridge.snapshot.push; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.InternalErrorException; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.PostbackTimeoutException; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; -import uk.ac.ic.wlgitbridge.snapshot.push.exception.InvalidPostbackKeyException; - import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.InternalErrorException; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.InvalidPostbackKeyException; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.PostbackTimeoutException; +import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; -/** +/* * Created by Winston on 17/11/14. */ public class PostbackPromise { - private static int TIMEOUT_SECONDS = 60 * 6; + private static int TIMEOUT_SECONDS = 60 * 6; - private final String postbackKey; - private final ReentrantLock lock; - private final Condition cond; + private final String postbackKey; + private final ReentrantLock lock; + private final Condition cond; - private boolean received; - private int versionID; - private SnapshotPostException exception; + private boolean received; + private int versionID; + private SnapshotPostException exception; - public PostbackPromise(String postbackKey) { - this.postbackKey = postbackKey; - lock = new ReentrantLock(); - cond = lock.newCondition(); - received = false; - exception = null; - } + public PostbackPromise(String postbackKey) { + this.postbackKey = postbackKey; + lock = new ReentrantLock(); + cond = lock.newCondition(); + received = false; + exception = null; + } - public int waitForPostback() throws SnapshotPostException { - lock.lock(); + public int waitForPostback() throws SnapshotPostException { + lock.lock(); + try { + while (!received) { try { - while (!received) { - try { - if (!cond.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - throw new PostbackTimeoutException(TIMEOUT_SECONDS); - } - } catch (InterruptedException e) { - throw new InternalErrorException(); - } - } - if (exception != null) { - throw exception; - } - return versionID; - } finally { - lock.unlock(); + if (!cond.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + throw new PostbackTimeoutException(TIMEOUT_SECONDS); + } + } catch (InterruptedException e) { + throw new InternalErrorException(); } + } + if (exception != null) { + throw exception; + } + return versionID; + } finally { + lock.unlock(); } + } - public void receivedVersionID(int versionID, String postbackKey) { - lock.lock(); - try { - if (postbackKey.equals(this.postbackKey)) { - this.versionID = versionID; - received = true; - cond.signalAll(); - } - } finally { - lock.unlock(); - } + public void receivedVersionID(int versionID, String postbackKey) { + lock.lock(); + try { + if (postbackKey.equals(this.postbackKey)) { + this.versionID = versionID; + received = true; + cond.signalAll(); + } + } finally { + lock.unlock(); } + } - public void receivedException( - SnapshotPostException exception, - String postbackKey - ) { - lock.lock(); - try { - if (postbackKey.equals(this.postbackKey)) { - this.exception = exception; - received = true; - cond.signalAll(); - } - } finally { - lock.unlock(); - } + public void receivedException(SnapshotPostException exception, String postbackKey) { + lock.lock(); + try { + if (postbackKey.equals(this.postbackKey)) { + this.exception = exception; + received = true; + cond.signalAll(); + } + } finally { + lock.unlock(); } + } - public void checkPostbackKey( - String postbackKey - ) throws InvalidPostbackKeyException { - if (!postbackKey.equals(this.postbackKey)) { - throw new InvalidPostbackKeyException(); - } + public void checkPostbackKey(String postbackKey) throws InvalidPostbackKeyException { + if (!postbackKey.equals(this.postbackKey)) { + throw new InvalidPostbackKeyException(); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushRequest.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushRequest.java index 03391c07f4..a54f7c0ecd 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushRequest.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushRequest.java @@ -8,47 +8,39 @@ import uk.ac.ic.wlgitbridge.snapshot.base.SnapshotAPIRequest; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 16/11/14. */ public class PushRequest extends SnapshotAPIRequest { - private static final String API_CALL = "/snapshots"; + private static final String API_CALL = "/snapshots"; - private final CandidateSnapshot candidateSnapshot; - private final String postbackKey; + private final CandidateSnapshot candidateSnapshot; + private final String postbackKey; - public PushRequest( - Credential oauth2, - CandidateSnapshot candidateSnapshot, - String postbackKey - ) { - super(candidateSnapshot.getProjectName(), API_CALL, oauth2); - this.candidateSnapshot = candidateSnapshot; - this.postbackKey = postbackKey; - Log.debug( - "PushRequest({}, {}, {})", - "oauth2: ", - "candidateSnapshot: " + candidateSnapshot, - "postbackKey: " + postbackKey - ); - } + public PushRequest(Credential oauth2, CandidateSnapshot candidateSnapshot, String postbackKey) { + super(candidateSnapshot.getProjectName(), API_CALL, oauth2); + this.candidateSnapshot = candidateSnapshot; + this.postbackKey = postbackKey; + Log.debug( + "PushRequest({}, {}, {})", + "oauth2: ", + "candidateSnapshot: " + candidateSnapshot, + "postbackKey: " + postbackKey); + } - @Override - protected HTTPMethod httpMethod() { - return HTTPMethod.POST; - } + @Override + protected HTTPMethod httpMethod() { + return HTTPMethod.POST; + } - @Override - protected String getPostBody() { - return candidateSnapshot.getJsonRepresentation(postbackKey).toString(); - } - - @Override - protected PushResult parseResponse( - JsonElement json - ) throws FailedConnectionException { - return new PushResult(this, json); - } + @Override + protected String getPostBody() { + return candidateSnapshot.getJsonRepresentation(postbackKey).toString(); + } + @Override + protected PushResult parseResponse(JsonElement json) throws FailedConnectionException { + return new PushResult(this, json); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushResult.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushResult.java index d7643da955..d35a724eb0 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushResult.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/PushResult.java @@ -2,47 +2,44 @@ package uk.ac.ic.wlgitbridge.snapshot.push; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import uk.ac.ic.wlgitbridge.snapshot.base.Result; import uk.ac.ic.wlgitbridge.snapshot.base.Request; +import uk.ac.ic.wlgitbridge.snapshot.base.Result; import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.util.Log; import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 16/11/14. */ public class PushResult extends Result { - private boolean success; + private boolean success; - public PushResult( - Request request, - JsonElement json - ) throws FailedConnectionException { - super(request, json); - } - - @Override - public JsonElement toJson() { - return null; - } - - public boolean wasSuccessful() { - return success; - } - - @Override - public void fromJSON(JsonElement json) { - Log.debug("PushResult({})", json); - JsonObject responseObject = json.getAsJsonObject(); - String code = Util.getCodeFromResponse(responseObject); - - if (code.equals("accepted")) { - success = true; - } else if (code.equals("outOfDate")) { - success = false; - } else { - throw new RuntimeException(); - } + public PushResult(Request request, JsonElement json) throws FailedConnectionException { + super(request, json); + } + + @Override + public JsonElement toJson() { + return null; + } + + public boolean wasSuccessful() { + return success; + } + + @Override + public void fromJSON(JsonElement json) { + Log.debug("PushResult({})", json); + JsonObject responseObject = json.getAsJsonObject(); + String code = Util.getCodeFromResponse(responseObject); + + if (code.equals("accepted")) { + success = true; + } else if (code.equals("outOfDate")) { + success = false; + } else { + throw new RuntimeException(); } + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InternalErrorException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InternalErrorException.java index 3d3dbd0f23..0627707c27 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InternalErrorException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InternalErrorException.java @@ -1,30 +1,27 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.util.Util; - import java.util.Arrays; import java.util.List; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 09/01/15. */ public class InternalErrorException extends SevereSnapshotPostException { - @Override - public String getMessage() { - return "internal error"; - } + @Override + public String getMessage() { + return "internal error"; + } - @Override - public List getDescriptionLines() { - return Arrays.asList( - "There was an internal error with the Git server.", - "Please contact " + Util.getServiceName() + "." - ); - } - - @Override - public void fromJSON(JsonElement json) {} + @Override + public List getDescriptionLines() { + return Arrays.asList( + "There was an internal error with the Git server.", + "Please contact " + Util.getServiceName() + "."); + } + @Override + public void fromJSON(JsonElement json) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidFilesException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidFilesException.java index 12d3f5ed9e..739c0e50a6 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidFilesException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidFilesException.java @@ -3,72 +3,67 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import uk.ac.ic.wlgitbridge.util.Util; - import java.util.ArrayList; import java.util.List; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 16/11/14. */ public class InvalidFilesException extends SnapshotPostException { - private List descriptionLines; + private List descriptionLines; - public InvalidFilesException(JsonObject json) { - super(json); + public InvalidFilesException(JsonObject json) { + super(json); + } + + @Override + public String getMessage() { + return "invalid files"; + } + + @Override + public List getDescriptionLines() { + return descriptionLines; + } + + @Override + public void fromJSON(JsonElement json) { + descriptionLines = new ArrayList<>(); + JsonArray errors = json.getAsJsonObject().get("errors").getAsJsonArray(); + descriptionLines.add( + "You have " + + errors.size() + + " invalid files in your " + + Util.getServiceName() + + " project:"); + for (JsonElement error : errors) { + descriptionLines.add(describeError(error.getAsJsonObject())); } + } - @Override - public String getMessage() { - return "invalid files"; + private String describeError(JsonObject jsonObject) { + return jsonObject.get("file").getAsString() + " (" + describeFile(jsonObject) + ")"; + } + + private String describeFile(JsonObject file) { + if (file.has("cleanFile")) { + return describeCleanFile(file.get("cleanFile").getAsString()); + } else { + return describeErrorState(file.get("state").getAsString()); } + } - @Override - public List getDescriptionLines() { - return descriptionLines; + private String describeCleanFile(String cleanFile) { + return "rename to: " + cleanFile; + } + + private String describeErrorState(String state) { + if (state.equals("disallowed")) { + return "invalid file extension"; + } else { + return "error"; } - - @Override - public void fromJSON(JsonElement json) { - descriptionLines = new ArrayList<>(); - JsonArray errors = - json.getAsJsonObject().get("errors").getAsJsonArray(); - descriptionLines.add( - "You have " - + errors.size() - + " invalid files in your " - + Util.getServiceName() - + " project:" - ); - for (JsonElement error : errors) { - descriptionLines.add(describeError(error.getAsJsonObject())); - } - } - - private String describeError(JsonObject jsonObject) { - return jsonObject.get("file").getAsString() - + " (" + describeFile(jsonObject) + ")"; - } - - private String describeFile(JsonObject file) { - if (file.has("cleanFile")) { - return describeCleanFile(file.get("cleanFile").getAsString()); - } else { - return describeErrorState(file.get("state").getAsString()); - } - } - - private String describeCleanFile(String cleanFile) { - return "rename to: " + cleanFile; - } - - private String describeErrorState(String state) { - if (state.equals("disallowed")) { - return "invalid file extension"; - } else { - return "error"; - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidPostbackKeyException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidPostbackKeyException.java index 49e20097ec..6fdf8ab16c 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidPostbackKeyException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidPostbackKeyException.java @@ -1,6 +1,6 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; -/** +/* * Created by Winston on 04/12/14. */ public class InvalidPostbackKeyException extends Exception {} diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidProjectException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidProjectException.java index 5a36d54bfb..093d01e071 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidProjectException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/InvalidProjectException.java @@ -2,39 +2,36 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonArray; import com.google.gson.JsonElement; - import java.util.LinkedList; import java.util.List; -/** +/* * Created by Winston on 16/11/14. */ public class InvalidProjectException extends SnapshotPostException { - private LinkedList descriptionLines; + private LinkedList descriptionLines; - public InvalidProjectException(JsonElement jsonElement) { - super(jsonElement); + public InvalidProjectException(JsonElement jsonElement) { + super(jsonElement); + } + + @Override + public String getMessage() { + return "invalid project"; + } + + @Override + public List getDescriptionLines() { + return descriptionLines; + } + + @Override + public void fromJSON(JsonElement json) { + descriptionLines = new LinkedList(); + JsonArray errors = json.getAsJsonObject().get("errors").getAsJsonArray(); + for (JsonElement error : errors) { + descriptionLines.add(error.getAsString()); } - - @Override - public String getMessage() { - return "invalid project"; - } - - @Override - public List getDescriptionLines() { - return descriptionLines; - } - - @Override - public void fromJSON(JsonElement json) { - descriptionLines = new LinkedList(); - JsonArray errors = - json.getAsJsonObject().get("errors").getAsJsonArray(); - for (JsonElement error : errors) { - descriptionLines.add(error.getAsString()); - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/OutOfDateException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/OutOfDateException.java index 1ae0852424..c3a96d9ac7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/OutOfDateException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/OutOfDateException.java @@ -2,32 +2,30 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonElement; import com.google.gson.JsonObject; - import java.util.Arrays; import java.util.List; -/** +/* * Created by Winston on 16/11/14. */ public class OutOfDateException extends SnapshotPostException { - public OutOfDateException(JsonObject json) { - super(json); - } + public OutOfDateException(JsonObject json) { + super(json); + } - public OutOfDateException() {} + public OutOfDateException() {} - @Override - public String getMessage() { - return "out of date"; - } + @Override + public String getMessage() { + return "out of date"; + } - @Override - public List getDescriptionLines() { - return Arrays.asList("out of date (shouldn't print this)"); - } - - @Override - public void fromJSON(JsonElement json) {} + @Override + public List getDescriptionLines() { + return Arrays.asList("out of date (shouldn't print this)"); + } + @Override + public void fromJSON(JsonElement json) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/PostbackTimeoutException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/PostbackTimeoutException.java index ca19448c3e..4ae2d02e18 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/PostbackTimeoutException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/PostbackTimeoutException.java @@ -1,38 +1,33 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonElement; -import uk.ac.ic.wlgitbridge.util.Util; - import java.util.Arrays; import java.util.List; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 09/01/15. */ public class PostbackTimeoutException extends SevereSnapshotPostException { - private int timeout; + private int timeout; - public PostbackTimeoutException(int timeout) { - this.timeout = timeout; - } + public PostbackTimeoutException(int timeout) { + this.timeout = timeout; + } - @Override - public String getMessage() { - return "Request timed out (after " + this.timeout + " seconds)"; - } + @Override + public String getMessage() { + return "Request timed out (after " + this.timeout + " seconds)"; + } - @Override - public List getDescriptionLines() { - return Arrays.asList( - "The " - + Util.getServiceName() - + " server is currently unavailable.", - "Please try again later." - ); - } - - @Override - public void fromJSON(JsonElement json) {} + @Override + public List getDescriptionLines() { + return Arrays.asList( + "The " + Util.getServiceName() + " server is currently unavailable.", + "Please try again later."); + } + @Override + public void fromJSON(JsonElement json) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SevereSnapshotPostException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SevereSnapshotPostException.java index fd389822f5..de3141bef3 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SevereSnapshotPostException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SevereSnapshotPostException.java @@ -2,17 +2,16 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonElement; -/** +/* * Created by Winston on 10/01/15. */ public abstract class SevereSnapshotPostException extends SnapshotPostException { - public SevereSnapshotPostException() { - super(); - } - - public SevereSnapshotPostException(JsonElement json) { - super(json); - } + public SevereSnapshotPostException() { + super(); + } + public SevereSnapshotPostException(JsonElement json) { + super(json); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostException.java index 0913e65ba3..1b32825e1d 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostException.java @@ -3,15 +3,14 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonElement; import uk.ac.ic.wlgitbridge.git.exception.SnapshotAPIException; -/** +/* * Created by Winston on 16/11/14. */ public abstract class SnapshotPostException extends SnapshotAPIException { - public SnapshotPostException() {} - - public SnapshotPostException(JsonElement jsonElement) { - fromJSON(jsonElement); - } + public SnapshotPostException() {} + public SnapshotPostException(JsonElement jsonElement) { + fromJSON(jsonElement); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostExceptionBuilder.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostExceptionBuilder.java index 97e4118368..7737959a60 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostExceptionBuilder.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/SnapshotPostExceptionBuilder.java @@ -2,31 +2,28 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonObject; -/** +/* * Created by Winston on 17/11/14. */ public class SnapshotPostExceptionBuilder { - private static final String CODE_ERROR_OUT_OF_DATE = "outOfDate"; - private static final String CODE_ERROR_INVALID_FILES = "invalidFiles"; - private static final String CODE_ERROR_INVALID_PROJECT = "invalidProject"; - private static final String CODE_ERROR_UNKNOWN = "error"; + private static final String CODE_ERROR_OUT_OF_DATE = "outOfDate"; + private static final String CODE_ERROR_INVALID_FILES = "invalidFiles"; + private static final String CODE_ERROR_INVALID_PROJECT = "invalidProject"; + private static final String CODE_ERROR_UNKNOWN = "error"; - public SnapshotPostException build( - String errorCode, - JsonObject json - ) throws UnexpectedPostbackException { - if (errorCode.equals(CODE_ERROR_OUT_OF_DATE)) { - return new OutOfDateException(json); - } else if (errorCode.equals(CODE_ERROR_INVALID_FILES)) { - return new InvalidFilesException(json); - } else if (errorCode.equals(CODE_ERROR_INVALID_PROJECT)) { - return new InvalidProjectException(json); - } else if (errorCode.equals(CODE_ERROR_UNKNOWN)) { - return new UnexpectedErrorException(json); - } else { - throw new UnexpectedPostbackException(); - } + public SnapshotPostException build(String errorCode, JsonObject json) + throws UnexpectedPostbackException { + if (errorCode.equals(CODE_ERROR_OUT_OF_DATE)) { + return new OutOfDateException(json); + } else if (errorCode.equals(CODE_ERROR_INVALID_FILES)) { + return new InvalidFilesException(json); + } else if (errorCode.equals(CODE_ERROR_INVALID_PROJECT)) { + return new InvalidProjectException(json); + } else if (errorCode.equals(CODE_ERROR_UNKNOWN)) { + return new UnexpectedErrorException(json); + } else { + throw new UnexpectedPostbackException(); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedErrorException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedErrorException.java index d6ca9578ec..f1542d5564 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedErrorException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedErrorException.java @@ -2,37 +2,34 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import uk.ac.ic.wlgitbridge.util.Util; - import java.util.Arrays; import java.util.List; +import uk.ac.ic.wlgitbridge.util.Util; -/** +/* * Created by Winston on 16/11/14. */ public class UnexpectedErrorException extends SevereSnapshotPostException { - private static final String[] DESCRIPTION_LINES = { - "There was an internal error with the " - + Util.getServiceName() + " server.", - "Please contact " + Util.getServiceName() + "." - }; + private static final String[] DESCRIPTION_LINES = { + "There was an internal error with the " + Util.getServiceName() + " server.", + "Please contact " + Util.getServiceName() + "." + }; - public UnexpectedErrorException(JsonObject json) { - super(json); - } + public UnexpectedErrorException(JsonObject json) { + super(json); + } - @Override - public String getMessage() { - return Util.getServiceName() + " error"; - } + @Override + public String getMessage() { + return Util.getServiceName() + " error"; + } - @Override - public List getDescriptionLines() { - return Arrays.asList(DESCRIPTION_LINES); - } - - @Override - public void fromJSON(JsonElement json) {} + @Override + public List getDescriptionLines() { + return Arrays.asList(DESCRIPTION_LINES); + } + @Override + public void fromJSON(JsonElement json) {} } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedPostbackException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedPostbackException.java index 1b7adb17d4..27322f27f7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedPostbackException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/push/exception/UnexpectedPostbackException.java @@ -1,6 +1,6 @@ package uk.ac.ic.wlgitbridge.snapshot.push.exception; -/** +/* * Created by Winston on 17/11/14. */ public class UnexpectedPostbackException extends Exception {} diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/Main.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/Main.java index 812da70341..450d014c40 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/Main.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/Main.java @@ -1,30 +1,22 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock; -import uk.ac.ic.wlgitbridge.snapshot.servermock.server.MockSnapshotServer; -import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIStateBuilder; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import uk.ac.ic.wlgitbridge.snapshot.servermock.server.MockSnapshotServer; +import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIStateBuilder; -/** +/* * Created by Winston on 10/01/15. */ public class Main { - public static void main(String[] args) throws FileNotFoundException { - MockSnapshotServer server = new MockSnapshotServer( - 60000, - new File("/Users/Roxy/Code/java/writelatex-git-bridge") - ); - server.setState( - new SnapshotAPIStateBuilder( - new FileInputStream( - new File("/Users/Roxy/Desktop/state.json") - ) - ).build() - ); - server.start(); - } - + public static void main(String[] args) throws FileNotFoundException { + MockSnapshotServer server = + new MockSnapshotServer(60000, new File("/Users/Roxy/Code/java/writelatex-git-bridge")); + server.setState( + new SnapshotAPIStateBuilder(new FileInputStream(new File("/Users/Roxy/Desktop/state.json"))) + .build()); + server.start(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/exception/InvalidAPICallException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/exception/InvalidAPICallException.java index ac80157ee1..af303be8c2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/exception/InvalidAPICallException.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/exception/InvalidAPICallException.java @@ -1,12 +1,11 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.exception; -/** +/* * Created by Winston on 09/01/15. */ public class InvalidAPICallException extends Exception { - public InvalidAPICallException(String target) { - super(target); - } - + public InvalidAPICallException(String target) { + super(target); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponse.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponse.java index fadad1deb0..2ffbefd921 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponse.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponse.java @@ -1,14 +1,13 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response; -/** +/* * Created by Winston on 09/01/15. */ public abstract class SnapshotResponse { - public abstract String respond(); - - public String postback() { - return null; - } + public abstract String respond(); + public String postback() { + return null; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponseBuilder.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponseBuilder.java index d122ad82ba..0e1f4bcb42 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponseBuilder.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/SnapshotResponseBuilder.java @@ -1,80 +1,60 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response; -import uk.ac.ic.wlgitbridge.snapshot.servermock.exception - .InvalidAPICallException; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.getdoc - .SnapshotGetDocResponse; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.getforver - .SnapshotGetForVerResponse; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.getsavedver - .SnapshotGetSavedVersResponse; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push - .SnapshotPushResponse; +import uk.ac.ic.wlgitbridge.snapshot.servermock.exception.InvalidAPICallException; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.getdoc.SnapshotGetDocResponse; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.getforver.SnapshotGetForVerResponse; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.getsavedver.SnapshotGetSavedVersResponse; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.SnapshotPushResponse; import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotResponseBuilder { - private SnapshotAPIState state; + private SnapshotAPIState state; - public SnapshotResponse buildWithTarget( - String target, - String method - ) throws InvalidAPICallException { - checkPrefix(target); - return parseTarget(target, target.split("/"), method); + public SnapshotResponse buildWithTarget(String target, String method) + throws InvalidAPICallException { + checkPrefix(target); + return parseTarget(target, target.split("/"), method); + } + + private void checkPrefix(String target) throws InvalidAPICallException { + if (!target.startsWith("/api/v0/docs/")) { + throw new InvalidAPICallException(target); } + } + + private SnapshotResponse parseTarget(String target, String[] parts, String method) + throws InvalidAPICallException { + String projectName = parts[4]; + if (parts.length == 5) { + if (method.equals("GET")) { + return new SnapshotGetDocResponse(state.getStateForGetDoc(projectName)); + } + } else if (parts.length == 6) { + String type = parts[5]; + if (type.equals("snapshots") && method.equals("POST")) { + return new SnapshotPushResponse( + state.getStateForPush(projectName), state.getStateForPostback(projectName)); + } else if (type.equals("saved_vers") && method.equals("GET")) { + return new SnapshotGetSavedVersResponse(state.getStateForGetSavedVers(projectName)); + } + } else if (parts.length == 7) { + if (parts[5].equals("snapshots") && method.equals("GET")) { + try { + return new SnapshotGetForVerResponse( + state.getStateForGetForVers(projectName, Integer.parseInt(parts[6]))); + } catch (NumberFormatException e) { - private void checkPrefix(String target) throws InvalidAPICallException { - if (!target.startsWith("/api/v0/docs/")) { - throw new InvalidAPICallException(target); } + } } + throw new InvalidAPICallException(target); + } - private SnapshotResponse parseTarget( - String target, - String[] parts, - String method - ) throws InvalidAPICallException { - String projectName = parts[4]; - if (parts.length == 5) { - if (method.equals("GET")) { - return new SnapshotGetDocResponse( - state.getStateForGetDoc(projectName) - ); - } - } else if (parts.length == 6) { - String type = parts[5]; - if (type.equals("snapshots") && method.equals("POST")) { - return new SnapshotPushResponse( - state.getStateForPush(projectName), - state.getStateForPostback(projectName) - ); - } else if (type.equals("saved_vers") && method.equals("GET")) { - return new SnapshotGetSavedVersResponse( - state.getStateForGetSavedVers(projectName) - ); - } - } else if (parts.length == 7) { - if (parts[5].equals("snapshots") && method.equals("GET")) { - try { - return new SnapshotGetForVerResponse( - state.getStateForGetForVers( - projectName, Integer.parseInt(parts[6]) - ) - ); - } catch (NumberFormatException e) { - - } - } - } - throw new InvalidAPICallException(target); - } - - public void setState(SnapshotAPIState state) { - this.state = state; - } - + public void setState(SnapshotAPIState state) { + this.state = state; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getdoc/SnapshotGetDocResponse.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getdoc/SnapshotGetDocResponse.java index eec526e328..11ab415e7b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getdoc/SnapshotGetDocResponse.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getdoc/SnapshotGetDocResponse.java @@ -1,22 +1,21 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.getdoc; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotGetDocResponse extends SnapshotResponse { - private final GetDocResult state; + private final GetDocResult state; - public SnapshotGetDocResponse(GetDocResult state) { - this.state = state; - } - - @Override - public String respond() { - return state.toJson().toString(); - } + public SnapshotGetDocResponse(GetDocResult state) { + this.state = state; + } + @Override + public String respond() { + return state.toJson().toString(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getforver/SnapshotGetForVerResponse.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getforver/SnapshotGetForVerResponse.java index 0266842ba1..38a4736f29 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getforver/SnapshotGetForVerResponse.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getforver/SnapshotGetForVerResponse.java @@ -1,22 +1,21 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.getforver; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; import uk.ac.ic.wlgitbridge.snapshot.getforversion.GetForVersionResult; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotGetForVerResponse extends SnapshotResponse { - private final GetForVersionResult state; + private final GetForVersionResult state; - public SnapshotGetForVerResponse(GetForVersionResult state) { - this.state = state; - } - - @Override - public String respond() { - return state.toJson().toString(); - } + public SnapshotGetForVerResponse(GetForVersionResult state) { + this.state = state; + } + @Override + public String respond() { + return state.toJson().toString(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getsavedver/SnapshotGetSavedVersResponse.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getsavedver/SnapshotGetSavedVersResponse.java index 08cc628a3e..a8da86b568 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getsavedver/SnapshotGetSavedVersResponse.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/getsavedver/SnapshotGetSavedVersResponse.java @@ -1,22 +1,21 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.getsavedver; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.GetSavedVersResult; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotGetSavedVersResponse extends SnapshotResponse { - private final GetSavedVersResult state; + private final GetSavedVersResult state; - public SnapshotGetSavedVersResponse(GetSavedVersResult state) { - this.state = state; - } - - @Override - public String respond() { - return state.toJson().toString(); - } + public SnapshotGetSavedVersResponse(GetSavedVersResult state) { + this.state = state; + } + @Override + public String respond() { + return state.toJson().toString(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/SnapshotPushResponse.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/SnapshotPushResponse.java index ec8ed23011..4568f608b4 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/SnapshotPushResponse.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/SnapshotPushResponse.java @@ -1,37 +1,34 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponse; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.SnapshotPostbackRequest; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data.SnapshotPushResult; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.SnapshotPostbackRequest; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotPushResponse extends SnapshotResponse { - private final SnapshotPushResult stateForPush; - private final SnapshotPostbackRequest stateForPostback; + private final SnapshotPushResult stateForPush; + private final SnapshotPostbackRequest stateForPostback; - public SnapshotPushResponse( - SnapshotPushResult stateForPush, - SnapshotPostbackRequest stateForPostback - ) { - this.stateForPush = stateForPush; - this.stateForPostback = stateForPostback; + public SnapshotPushResponse( + SnapshotPushResult stateForPush, SnapshotPostbackRequest stateForPostback) { + this.stateForPush = stateForPush; + this.stateForPostback = stateForPostback; + } + + @Override + public String respond() { + return stateForPush.toJson().toString(); + } + + @Override + public String postback() { + if (stateForPush.hasPostback()) { + return stateForPostback.toJson().toString(); + } else { + return null; } - - @Override - public String respond() { - return stateForPush.toJson().toString(); - } - - @Override - public String postback() { - if (stateForPush.hasPostback()) { - return stateForPostback.toJson().toString(); - } else { - return null; - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResult.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResult.java index 81b6d93c46..cdcd893530 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResult.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResult.java @@ -3,29 +3,28 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -/** +/* * Created by Winston on 09/01/15. */ public abstract class SnapshotPushResult { - private final int status; - private final String code; - private final String message; + private final int status; + private final String code; + private final String message; - public SnapshotPushResult(int status, String code, String message) { - this.status = status; - this.code = code; - this.message = message; - } + public SnapshotPushResult(int status, String code, String message) { + this.status = status; + this.code = code; + this.message = message; + } - public JsonElement toJson() { - JsonObject jsonThis = new JsonObject(); - jsonThis.addProperty("status", status); - jsonThis.addProperty("code", code); - jsonThis.addProperty("message", message); - return jsonThis; - } - - public abstract boolean hasPostback(); + public JsonElement toJson() { + JsonObject jsonThis = new JsonObject(); + jsonThis.addProperty("status", status); + jsonThis.addProperty("code", code); + jsonThis.addProperty("message", message); + return jsonThis; + } + public abstract boolean hasPostback(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultOutOfDate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultOutOfDate.java index 26edb59fb5..d6b7eea027 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultOutOfDate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultOutOfDate.java @@ -1,17 +1,16 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotPushResultOutOfDate extends SnapshotPushResult { - public SnapshotPushResultOutOfDate() { - super(409, "outOfDate", "Out of Date"); - } - - @Override - public boolean hasPostback() { - return false; - } + public SnapshotPushResultOutOfDate() { + super(409, "outOfDate", "Out of Date"); + } + @Override + public boolean hasPostback() { + return false; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultSuccess.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultSuccess.java index 327dabaa86..948105b3d2 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultSuccess.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/data/SnapshotPushResultSuccess.java @@ -1,17 +1,16 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotPushResultSuccess extends SnapshotPushResult { - public SnapshotPushResultSuccess() { - super(402, "accepted", "Accepted"); - } - - @Override - public boolean hasPostback() { - return true; - } + public SnapshotPushResultSuccess() { + super(402, "accepted", "Accepted"); + } + @Override + public boolean hasPostback() { + return true; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequest.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequest.java index 9799f8f4f0..8560714e6b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequest.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequest.java @@ -2,21 +2,20 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback; import com.google.gson.JsonObject; -/** +/* * Created by Winston on 09/01/15. */ public abstract class SnapshotPostbackRequest { - private final String code; + private final String code; - public SnapshotPostbackRequest(String code) { - this.code = code; - } - - public JsonObject toJson() { - JsonObject jsonThis = new JsonObject(); - jsonThis.addProperty("code", code); - return jsonThis; - } + public SnapshotPostbackRequest(String code) { + this.code = code; + } + public JsonObject toJson() { + JsonObject jsonThis = new JsonObject(); + jsonThis.addProperty("code", code); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestError.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestError.java index 2d68fafa33..27e4b7b1b1 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestError.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestError.java @@ -2,20 +2,19 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback; import com.google.gson.JsonObject; -/** +/* * Created by Winston on 10/01/15. */ public class SnapshotPostbackRequestError extends SnapshotPostbackRequest { - public SnapshotPostbackRequestError() { - super("error"); - } - - @Override - public JsonObject toJson() { - JsonObject jsonThis = super.toJson(); - jsonThis.addProperty("message", "Unexpected Error"); - return jsonThis; - } + public SnapshotPostbackRequestError() { + super("error"); + } + @Override + public JsonObject toJson() { + JsonObject jsonThis = super.toJson(); + jsonThis.addProperty("message", "Unexpected Error"); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidFiles.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidFiles.java index dd1d24db22..e4bd120b41 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidFiles.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidFiles.java @@ -3,41 +3,37 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.invalidfile.InvalidFileError; - import java.util.ArrayList; import java.util.List; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.invalidfile.InvalidFileError; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotPostbackRequestInvalidFiles extends SnapshotPostbackRequest { - private final List errors; + private final List errors; - public SnapshotPostbackRequestInvalidFiles(List errors) { - super("invalidFiles"); - this.errors = errors; + public SnapshotPostbackRequestInvalidFiles(List errors) { + super("invalidFiles"); + this.errors = errors; + } + + public SnapshotPostbackRequestInvalidFiles(JsonArray errors) { + this(new ArrayList()); + for (JsonElement error : errors) { + this.errors.add(InvalidFileError.buildFromJsonError(error.getAsJsonObject())); } + } - public SnapshotPostbackRequestInvalidFiles(JsonArray errors) { - this(new ArrayList()); - for (JsonElement error : errors) { - this.errors.add(InvalidFileError.buildFromJsonError( - error.getAsJsonObject() - )); - } + @Override + public JsonObject toJson() { + JsonObject jsonThis = super.toJson(); + JsonArray jsonErrors = new JsonArray(); + for (InvalidFileError error : errors) { + jsonErrors.add(error.toJson()); } - - @Override - public JsonObject toJson() { - JsonObject jsonThis = super.toJson(); - JsonArray jsonErrors = new JsonArray(); - for (InvalidFileError error : errors) { - jsonErrors.add(error.toJson()); - } - jsonThis.add("errors", jsonErrors); - return jsonThis; - } - + jsonThis.add("errors", jsonErrors); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidProject.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidProject.java index e870b43158..fbdfabe931 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidProject.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestInvalidProject.java @@ -4,40 +4,37 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; - import java.util.ArrayList; import java.util.List; -/** +/* * Created by Winston on 10/01/15. */ -public class SnapshotPostbackRequestInvalidProject - extends SnapshotPostbackRequest { +public class SnapshotPostbackRequestInvalidProject extends SnapshotPostbackRequest { - private final List errors; + private final List errors; - public SnapshotPostbackRequestInvalidProject(List errors) { - super("invalidProject"); - this.errors = errors; + public SnapshotPostbackRequestInvalidProject(List errors) { + super("invalidProject"); + this.errors = errors; + } + + public SnapshotPostbackRequestInvalidProject(JsonArray errors) { + this(new ArrayList()); + for (JsonElement error : errors) { + this.errors.add(error.getAsString()); } + } - public SnapshotPostbackRequestInvalidProject(JsonArray errors) { - this(new ArrayList()); - for (JsonElement error : errors) { - this.errors.add(error.getAsString()); - } + @Override + public JsonObject toJson() { + JsonObject jsonThis = super.toJson(); + jsonThis.addProperty("message", "short string message for debugging"); + JsonArray jsonErrors = new JsonArray(); + for (String error : errors) { + jsonErrors.add(new JsonPrimitive(error)); } - - @Override - public JsonObject toJson() { - JsonObject jsonThis = super.toJson(); - jsonThis.addProperty("message", "short string message for debugging"); - JsonArray jsonErrors = new JsonArray(); - for (String error : errors) { - jsonErrors.add(new JsonPrimitive(error)); - } - jsonThis.add("errors", jsonErrors); - return jsonThis; - } - + jsonThis.add("errors", jsonErrors); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestOutOfDate.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestOutOfDate.java index def04e92b1..43453e824b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestOutOfDate.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestOutOfDate.java @@ -2,20 +2,19 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback; import com.google.gson.JsonObject; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotPostbackRequestOutOfDate extends SnapshotPostbackRequest { - public SnapshotPostbackRequestOutOfDate() { - super("outOfDate"); - } - - @Override - public JsonObject toJson() { - JsonObject jsonThis = super.toJson(); - jsonThis.addProperty("message", "Out of Date"); - return jsonThis; - } + public SnapshotPostbackRequestOutOfDate() { + super("outOfDate"); + } + @Override + public JsonObject toJson() { + JsonObject jsonThis = super.toJson(); + jsonThis.addProperty("message", "Out of Date"); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestSuccess.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestSuccess.java index 07dba7e5fd..13778ca16a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestSuccess.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/SnapshotPostbackRequestSuccess.java @@ -2,23 +2,22 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback; import com.google.gson.JsonObject; -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotPostbackRequestSuccess extends SnapshotPostbackRequest { - private final int latestVerId; + private final int latestVerId; - public SnapshotPostbackRequestSuccess(int latestVerId) { - super("upToDate"); - this.latestVerId = latestVerId; - } - - @Override - public JsonObject toJson() { - JsonObject jsonThis = super.toJson(); - jsonThis.addProperty("latestVerId", latestVerId); - return jsonThis; - } + public SnapshotPostbackRequestSuccess(int latestVerId) { + super("upToDate"); + this.latestVerId = latestVerId; + } + @Override + public JsonObject toJson() { + JsonObject jsonThis = super.toJson(); + jsonThis.addProperty("latestVerId", latestVerId); + return jsonThis; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileError.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileError.java index b2b92eb575..670326d15e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileError.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileError.java @@ -2,42 +2,37 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.invalidf import com.google.gson.JsonObject; -/** +/* * Created by Winston on 09/01/15. */ public abstract class InvalidFileError { - private final String file; + private final String file; - public InvalidFileError(String file) { - this.file = file; + public InvalidFileError(String file) { + this.file = file; + } + + public JsonObject toJson() { + JsonObject jsonThis = new JsonObject(); + jsonThis.addProperty("file", file); + jsonThis.addProperty("state", getState()); + return jsonThis; + } + + protected abstract String getState(); + + public static InvalidFileError buildFromJsonError(JsonObject error) { + String state = error.get("state").getAsString(); + String file = error.get("file").getAsString(); + if (state.equals("error")) { + return new InvalidFileErrorDefault(file); + } else if (state.equals("disallowed")) { + return new InvalidFileErrorDisallowed(file); + } else if (state.equals("unclean_name")) { + return new InvalidFileErrorUnclean(file, error.get("cleanFile").getAsString()); + } else { + throw new IllegalArgumentException("bad invalid file state: " + state); } - - public JsonObject toJson() { - JsonObject jsonThis = new JsonObject(); - jsonThis.addProperty("file", file); - jsonThis.addProperty("state", getState()); - return jsonThis; - } - - protected abstract String getState(); - - public static InvalidFileError buildFromJsonError(JsonObject error) { - String state = error.get("state").getAsString(); - String file = error.get("file").getAsString(); - if (state.equals("error")) { - return new InvalidFileErrorDefault(file); - } else if (state.equals("disallowed")) { - return new InvalidFileErrorDisallowed(file); - } else if (state.equals("unclean_name")) { - return new InvalidFileErrorUnclean( - file, error.get("cleanFile").getAsString() - ); - } else { - throw new IllegalArgumentException( - "bad invalid file state: " + state - ); - } - } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDefault.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDefault.java index 85fc037088..644d1659ad 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDefault.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDefault.java @@ -1,17 +1,16 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.invalidfile; -/** +/* * Created by Winston on 09/01/15. */ public class InvalidFileErrorDefault extends InvalidFileError { - public InvalidFileErrorDefault(String file) { - super(file); - } - - @Override - protected String getState() { - return "error"; - } + public InvalidFileErrorDefault(String file) { + super(file); + } + @Override + protected String getState() { + return "error"; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDisallowed.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDisallowed.java index 159f80406c..fad3ac1f7d 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDisallowed.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorDisallowed.java @@ -1,17 +1,16 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.invalidfile; -/** +/* * Created by Winston on 09/01/15. */ public class InvalidFileErrorDisallowed extends InvalidFileError { - public InvalidFileErrorDisallowed(String file) { - super(file); - } - - @Override - protected String getState() { - return "disallowed"; - } + public InvalidFileErrorDisallowed(String file) { + super(file); + } + @Override + protected String getState() { + return "disallowed"; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorUnclean.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorUnclean.java index 746dc766a2..9c2c75b549 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorUnclean.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/response/push/postback/invalidfile/InvalidFileErrorUnclean.java @@ -2,28 +2,27 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.invalidf import com.google.gson.JsonObject; -/** +/* * Created by Winston on 09/01/15. */ public class InvalidFileErrorUnclean extends InvalidFileError { - private final String cleanFile; + private final String cleanFile; - public InvalidFileErrorUnclean(String file, String cleanFile) { - super(file); - this.cleanFile = cleanFile; - } + public InvalidFileErrorUnclean(String file, String cleanFile) { + super(file); + this.cleanFile = cleanFile; + } - @Override - public JsonObject toJson() { - JsonObject jsonThis = super.toJson(); - jsonThis.addProperty("cleanFile", cleanFile); - return jsonThis; - } - - @Override - protected String getState() { - return "unclean_name"; - } + @Override + public JsonObject toJson() { + JsonObject jsonThis = super.toJson(); + jsonThis.addProperty("cleanFile", cleanFile); + return jsonThis; + } + @Override + protected String getState() { + return "unclean_name"; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockOAuthRequestHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockOAuthRequestHandler.java index 48600f8c12..278e15f16f 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockOAuthRequestHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockOAuthRequestHandler.java @@ -3,26 +3,21 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.server; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.Request; -import uk.ac.ic.wlgitbridge.util.Log; +import org.eclipse.jetty.server.handler.AbstractHandler; public class MockOAuthRequestHandler extends AbstractHandler { - @Override - public void handle( - String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - String method = baseRequest.getMethod(); - if (method.equals("GET") && target.equals("/oauth/token/info")) { - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_OK); - response.getWriter().println("{}"); - baseRequest.setHandled(true); - } + @Override + public void handle( + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException { + String method = baseRequest.getMethod(); + if (method.equals("GET") && target.equals("/oauth/token/info")) { + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println("{}"); + baseRequest.setHandled(true); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotRequestHandler.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotRequestHandler.java index 649a4450b5..53727cf2ab 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotRequestHandler.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotRequestHandler.java @@ -1,56 +1,46 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.server; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import uk.ac.ic.wlgitbridge.snapshot.servermock.exception. - InvalidAPICallException; -import uk.ac.ic.wlgitbridge.snapshot.servermock.response.*; -import uk.ac.ic.wlgitbridge.util.Log; - +import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import uk.ac.ic.wlgitbridge.snapshot.servermock.exception.InvalidAPICallException; +import uk.ac.ic.wlgitbridge.snapshot.servermock.response.*; +import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 09/01/15. */ public class MockSnapshotRequestHandler extends AbstractHandler { - private final SnapshotResponseBuilder responseBuilder; + private final SnapshotResponseBuilder responseBuilder; - public MockSnapshotRequestHandler( - SnapshotResponseBuilder responseBuilder - ) { - this.responseBuilder = responseBuilder; + public MockSnapshotRequestHandler(SnapshotResponseBuilder responseBuilder) { + this.responseBuilder = responseBuilder; + } + + @Override + public void handle( + String target, + final Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) + throws IOException, ServletException { + boolean handled; + try { + final SnapshotResponse snapshotResponse = + responseBuilder.buildWithTarget(target, baseRequest.getMethod()); + response.getWriter().println(snapshotResponse.respond()); + new PostbackThread(baseRequest.getReader(), snapshotResponse.postback()).startIfNotNull(); + handled = true; + } catch (InvalidAPICallException e) { + handled = false; + } catch (RuntimeException e) { + Log.warn("Runtime exception when handling request", e); + handled = true; } - - @Override - public void handle( - String target, - final Request baseRequest, - HttpServletRequest request, - HttpServletResponse response - ) throws IOException, ServletException { - boolean handled; - try { - final SnapshotResponse snapshotResponse - = responseBuilder.buildWithTarget( - target, baseRequest.getMethod() - ); - response.getWriter().println(snapshotResponse.respond()); - new PostbackThread( - baseRequest.getReader(), - snapshotResponse.postback() - ).startIfNotNull(); - handled = true; - } catch (InvalidAPICallException e) { - handled = false; - } catch (RuntimeException e) { - Log.warn("Runtime exception when handling request", e); - handled = true; - } - baseRequest.setHandled(handled); - } - + baseRequest.setHandled(handled); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotServer.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotServer.java index aeccb81253..2f1c0d5886 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotServer.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotServer.java @@ -1,5 +1,6 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.server; +import java.io.File; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerCollection; @@ -9,59 +10,56 @@ import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponseBuilder import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState; import uk.ac.ic.wlgitbridge.util.Log; -import java.io.File; - -/** +/* * Created by Winston on 09/01/15. */ public class MockSnapshotServer { - private final Server server; - private final SnapshotResponseBuilder responseBuilder; - private int port; + private final Server server; + private final SnapshotResponseBuilder responseBuilder; + private int port; - public MockSnapshotServer(int port, File resourceBase) { - server = new Server(port); - responseBuilder = new SnapshotResponseBuilder(); + public MockSnapshotServer(int port, File resourceBase) { + server = new Server(port); + responseBuilder = new SnapshotResponseBuilder(); - HandlerList handlers = new HandlerList(); - handlers.addHandler(new MockOAuthRequestHandler()); - handlers.addHandler(getHandlerForResourceBase(resourceBase)); - server.setHandler(handlers); + HandlerList handlers = new HandlerList(); + handlers.addHandler(new MockOAuthRequestHandler()); + handlers.addHandler(getHandlerForResourceBase(resourceBase)); + server.setHandler(handlers); + } + + private HandlerCollection getHandlerForResourceBase(File resourceBase) { + HandlerCollection handlers = new HandlerCollection(); + handlers.addHandler(new MockSnapshotRequestHandler(responseBuilder)); + handlers.addHandler(resourceHandlerWithBase(resourceBase)); + return handlers; + } + + private ResourceHandler resourceHandlerWithBase(File resourceBase) { + ResourceHandler resourceHandler = new ResourceHandler(); + resourceHandler.setResourceBase(resourceBase.getAbsolutePath()); + return resourceHandler; + } + + public void start() { + try { + server.start(); + } catch (Exception e) { + Log.warn("Exception when trying to start server", e); } + port = ((NetworkConnector) server.getConnectors()[0]).getLocalPort(); + } - private HandlerCollection getHandlerForResourceBase(File resourceBase) { - HandlerCollection handlers = new HandlerCollection(); - handlers.addHandler(new MockSnapshotRequestHandler(responseBuilder)); - handlers.addHandler(resourceHandlerWithBase(resourceBase)); - return handlers; - } - - private ResourceHandler resourceHandlerWithBase(File resourceBase) { - ResourceHandler resourceHandler = new ResourceHandler(); - resourceHandler.setResourceBase(resourceBase.getAbsolutePath()); - return resourceHandler; - } - - public void start() { - try { - server.start(); - } catch (Exception e) { - Log.warn("Exception when trying to start server", e); - } - port = ((NetworkConnector) server.getConnectors()[0]).getLocalPort(); - } - - public void stop() { - try { - server.stop(); - } catch (Exception e) { - Log.warn("Exception when trying to stop server", e); - } - } - - public void setState(SnapshotAPIState state) { - responseBuilder.setState(state); + public void stop() { + try { + server.stop(); + } catch (Exception e) { + Log.warn("Exception when trying to stop server", e); } + } + public void setState(SnapshotAPIState state) { + responseBuilder.setState(state); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/PostbackThread.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/PostbackThread.java index c3559552ed..0e105ef90a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/PostbackThread.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/PostbackThread.java @@ -1,61 +1,42 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.server; +import static org.asynchttpclient.Dsl.*; + import com.google.gson.Gson; import com.google.gson.JsonObject; -import static org.asynchttpclient.Dsl.*; -import uk.ac.ic.wlgitbridge.util.Log; - -import java.io.IOException; import java.io.Reader; import java.util.concurrent.ExecutionException; +import uk.ac.ic.wlgitbridge.util.Log; -/** +/* * Created by Winston on 10/01/15. */ public class PostbackThread extends Thread { - private String url; - private String postback; + private String url; + private String postback; - public PostbackThread(Reader reader, String postback) { - if (postback != null) { - url = new Gson().fromJson( - reader, - JsonObject.class - ).get("postbackUrl").getAsString(); - this.postback = postback; - } + public PostbackThread(Reader reader, String postback) { + if (postback != null) { + url = new Gson().fromJson(reader, JsonObject.class).get("postbackUrl").getAsString(); + this.postback = postback; } + } - @Override - public void run() { - try { - asyncHttpClient().preparePost( - url - ).setBody(postback).execute().get().getResponseBody(); - } catch (InterruptedException e) { - Log.warn( - "Interrupted on postback, url: " + - url + - ", postback: " + - postback, - e - ); - } catch (ExecutionException e) { - Log.warn( - "ExecutionException on postback, url: " + - url + - ", postback: " + - postback, - e - ); - } + @Override + public void run() { + try { + asyncHttpClient().preparePost(url).setBody(postback).execute().get().getResponseBody(); + } catch (InterruptedException e) { + Log.warn("Interrupted on postback, url: " + url + ", postback: " + postback, e); + } catch (ExecutionException e) { + Log.warn("ExecutionException on postback, url: " + url + ", postback: " + postback, e); } + } - public void startIfNotNull() { - if (url != null && postback != null) { - start(); - } + public void startIfNotNull() { + if (url != null && postback != null) { + start(); } - + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIState.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIState.java index 02f8e19a8b..fa8c9b716a 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIState.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIState.java @@ -1,189 +1,271 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.state; +import java.util.*; +import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; +import uk.ac.ic.wlgitbridge.snapshot.getforversion.GetForVersionResult; +import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotAttachment; +import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotData; +import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotFile; +import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.GetSavedVersResult; +import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.SnapshotInfo; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data.SnapshotPushResult; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data.SnapshotPushResultSuccess; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.SnapshotPostbackRequest; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.SnapshotPostbackRequestInvalidProject; -import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; -import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotAttachment; -import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotData; -import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotFile; -import uk.ac.ic.wlgitbridge.snapshot.getforversion.GetForVersionResult; -import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.GetSavedVersResult; -import uk.ac.ic.wlgitbridge.snapshot.getsavedvers.SnapshotInfo; -import java.util.*; - -/** +/* * Created by Winston on 09/01/15. */ public class SnapshotAPIState { - private Map getDoc; - private Map getSavedVers; - private Map> getForVers; - private Map push; - private Map postback; + private Map getDoc; + private Map getSavedVers; + private Map> getForVers; + private Map push; + private Map postback; - public SnapshotAPIState( - Map getDoc, - Map getSavedVers, - Map> getForVers, - Map push, - Map postback - ) { - this.getDoc = getDoc; - this.getSavedVers = getSavedVers; - this.getForVers = getForVers; - this.push = push; - this.postback = postback; - } + public SnapshotAPIState( + Map getDoc, + Map getSavedVers, + Map> getForVers, + Map push, + Map postback) { + this.getDoc = getDoc; + this.getSavedVers = getSavedVers; + this.getForVers = getForVers; + this.push = push; + this.postback = postback; + } - public SnapshotAPIState() { - getDoc = new HashMap<>(); - getDoc.put( - "1826rqgsdb", - new GetDocResult( - null, - 243, - "2014-11-30T18:40:58Z", - "jdleesmiller+1@gmail.com", - "John+1" - ) - ); + public SnapshotAPIState() { + getDoc = new HashMap<>(); + getDoc.put( + "1826rqgsdb", + new GetDocResult(null, 243, "2014-11-30T18:40:58Z", "jdleesmiller+1@gmail.com", "John+1")); - getSavedVers = new HashMap(); - List savedVers = new LinkedList(); - savedVers.add(new SnapshotInfo( - 243, - "added more info on doc GET and error details", - "jdleesmiller+1@gmail.com", - "John+1", - "2014-11-30T18:47:01Z" - )); - savedVers.add(new SnapshotInfo( - 185, "with more details on POST request", "jdleesmiller+1@gmail.com", "John+1", "2014-11-11T17:18:40Z" - )); - savedVers.add(new SnapshotInfo( - 175, "with updated PUT/POST request", "jdleesmiller+1@gmail.com", "John+1", "2014-11-09T23:09:13Z" - )); - savedVers.add(new SnapshotInfo( - 146, "added PUT format", "jdleesmiller@gmail.com", "John Lees-Miller", "2014-11-07T15:11:35Z" - )); - savedVers.add(new SnapshotInfo( - 74, "with example output", "jdleesmiller@gmail.com", "John Lees-Miller", "2014-11-05T18:09:41Z" - )); - savedVers.add(new SnapshotInfo( - 39, "with more files", "jdleesmiller@gmail.com", "John Lees-Miller", "2014-11-05T18:02:19Z" - )); - savedVers.add(new SnapshotInfo( - 24, "first draft", "jdleesmiller@gmail.com", "John Lees-Miller", "2014-11-05T17:56:58Z" - )); - getSavedVers.put("1826rqgsdb", new GetSavedVersResult(savedVers)); + getSavedVers = new HashMap(); + List savedVers = new LinkedList(); + savedVers.add( + new SnapshotInfo( + 243, + "added more info on doc GET and error details", + "jdleesmiller+1@gmail.com", + "John+1", + "2014-11-30T18:47:01Z")); + savedVers.add( + new SnapshotInfo( + 185, + "with more details on POST request", + "jdleesmiller+1@gmail.com", + "John+1", + "2014-11-11T17:18:40Z")); + savedVers.add( + new SnapshotInfo( + 175, + "with updated PUT/POST request", + "jdleesmiller+1@gmail.com", + "John+1", + "2014-11-09T23:09:13Z")); + savedVers.add( + new SnapshotInfo( + 146, + "added PUT format", + "jdleesmiller@gmail.com", + "John Lees-Miller", + "2014-11-07T15:11:35Z")); + savedVers.add( + new SnapshotInfo( + 74, + "with example output", + "jdleesmiller@gmail.com", + "John Lees-Miller", + "2014-11-05T18:09:41Z")); + savedVers.add( + new SnapshotInfo( + 39, + "with more files", + "jdleesmiller@gmail.com", + "John Lees-Miller", + "2014-11-05T18:02:19Z")); + savedVers.add( + new SnapshotInfo( + 24, + "first draft", + "jdleesmiller@gmail.com", + "John Lees-Miller", + "2014-11-05T17:56:58Z")); + getSavedVers.put("1826rqgsdb", new GetSavedVersResult(savedVers)); - getForVers = new HashMap>() {{ - put("1826rqgsdb", new HashMap() {{ - put(243, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. This endpoint returns the latest version id, when the latest version was created (ISO8601), and the user that last edited the project (if any, otherwise null).\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => {\\n \\\"latestVerId\\\": 39,\\n \\\"latestVerAt\\\": \\\"2014-11-30T18:35:27Z\\\",\\n \\\"latestVerBy\\\": {\\n \\\"email\\\": \\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\": \\\"John Lees-Miller\\\"\\n }}\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\n# NB: JLM originally said PUT, but he now thinks POST is better\\n# NB: you must set a Content-Type: application/json header for this request\\n# in order to specify the data as JSON in the request body\\nPOST https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 202,\\n code: \\\"accepted\\\",\\n message: \\\"Accepted\\\"\\n}\\nResponse on out of date:\\n{\\n status: 409, # Conflict\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error with the files list (e.g. file extension not allowed):\\n{\\n code: \\\"invalidFiles\\\",\\n errors: [ {\\n file: the file name from the snapshot,\\n state: \\\"error\\\"|\\\"disallowed\\\"|\\\"unclean_name\\\"\\n }, ... ]\\n}\\nIf the file's error state is unclean_name, the error object will alsocontain a property cleanFile that contains the name of the file after it has been \\\"cleaned\\\" to meet our file naming requirements; for other file error states, this property is not present.\\nOn error with the project as a whole (e.g. over quota):\\n{\\n code: \\\"invalidProject\\\",\\n message: short string message for debugging\\n errors: [ array of zero or more string messages for the user ]\\n}\\nOn unexpected error (bug):\\n{\\n code: \\\"error\\\",\\n message: \\\"Unexpected Error\\\"\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex"), - new SnapshotFile("This text is from another file.", "foo/bar/servermock.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - put(185, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\nTODO will also include updatedAt time and user (if it was not anonymous)\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\n# NB: JLM originally said PUT, but he now thinks POST is better\\n# NB: you must set a Content-Type: application/json header for this request\\n# in order to specify the data as JSON in the request body\\nPOST https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 202,\\n code: \\\"accepted\\\",\\n message: \\\"Accepted\\\"\\n}\\nResponse on out of date:\\n{\\n status: 409, # Conflict\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error with the files list (e.g. file extension not allowed):\\n{\\n code: \\\"invalidFiles\\\",\\n errors: TODO\\n}\\nOn error with the project as a whole (e.g. over quota):\\n{\\n code: \\\"invalidProject\\\",\\n errors: TODO\\n}\\nOn unexpected error (bug):\\n{\\n code: \\\"error\\\",\\n message: \\\"Unexpected Error\\\"\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex"), - new SnapshotFile("This text is from another file.", "foo/bar/servermock.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - put(175, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\nTODO will also include updatedAt time and user (if it was not anonymous)\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\n# NB: JLM originally said PUT, but he now thinks POST is better\\n# NB: you must set a Content-Type: application/json header for this request\\n# in order to specify the data as JSON in the request body\\nPOST https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 202,\\n code: \\\"accepted\\\",\\n message: \\\"Accepted\\\"\\n}\\nResponse on out of date:\\n{\\n status: 409, # Conflict\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error:\\n{\\n code: \\\"invalidFile\\\",\\n TODO\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex"), - new SnapshotFile("This text is from another file.", "foo/bar/servermock.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - put(146, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\nPUT https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 20x,\\n}\\nResponse on out of date:\\n{\\n status: 40x,\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error:\\n{\\n code: \\\"invalidFile\\\",\\n TODO\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex"), - new SnapshotFile("This text is from another file.", "foo/bar/servermock.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - put(74, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\nTODO still working on this part\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as a servermock project. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex"), - new SnapshotFile("This text is from another file.", "foo/bar/servermock.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - put(39, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. To list saved versions for a doc:\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/1\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\nTODO still working on this part\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as a servermock project. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex"), - new SnapshotFile("This text is from another file.", "foo/bar/servermock.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - put(24, new GetForVersionResult(new SnapshotData(Arrays.asList( - new SnapshotFile("\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server (radiant-wind-3058.herokuapp.com).\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://radiant-wind.....com/api/v0/docs/1826rqgsdb\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. To list saved versions for a doc:\\n\\n\\\\begin{lstlisting}\\nGET https://radiant-wind.....com/api/v0/docs/1826rqgsdb/saved_vers\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\n\\\\begin{lstlisting}\\nGET https://radiant-wind.....com/api/v0/docs/1826rqgsdb/snapshots/1\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\nTODO still working on this part\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as a servermock project. Here is an extra file to make it a bit more interesting.\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", "main.tex") - ), Arrays.asList( - new SnapshotAttachment("https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", "min_mean_wait_evm_7_eps_150dpi.png") - )))); - }}); - }}; - - push = new HashMap() {{ - put("1826rqgsdb", new SnapshotPushResultSuccess()); - }}; - - postback = new HashMap() {{ -// put( -// "1826rqgsdb", -// new SnapshotPostbackRequestInvalidFiles( -// Arrays.asList( -// new InvalidFileErrorDefault( -// "file1.invalid" -// ), -// new InvalidFileErrorDisallowed( -// "file2.exe" -// ), -// new InvalidFileErrorUnclean( -// "hello world.png", -// "hello_world.png" -// ) -// ) -// ) -// ); -// put("1826rqgsdb", new SnapshotPostbackRequestOutOfDate()); + getForVers = + new HashMap>() { + { put( - "1826rqgsdb", - new SnapshotPostbackRequestInvalidProject( - Arrays.asList( - "Your project is missing main.tex.", - "Please name your main latex file main.tex." - ) - ) - ); -// put("1826rqgsdb", new SnapshotPostbackRequestError()); - }}; - } + "1826rqgsdb", + new HashMap() { + { + put( + 243, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. This endpoint returns the latest version id, when the latest version was created (ISO8601), and the user that last edited the project (if any, otherwise null).\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => {\\n \\\"latestVerId\\\": 39,\\n \\\"latestVerAt\\\": \\\"2014-11-30T18:35:27Z\\\",\\n \\\"latestVerBy\\\": {\\n \\\"email\\\": \\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\": \\\"John Lees-Miller\\\"\\n }}\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\n# NB: JLM originally said PUT, but he now thinks POST is better\\n# NB: you must set a Content-Type: application/json header for this request\\n# in order to specify the data as JSON in the request body\\nPOST https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 202,\\n code: \\\"accepted\\\",\\n message: \\\"Accepted\\\"\\n}\\nResponse on out of date:\\n{\\n status: 409, # Conflict\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error with the files list (e.g. file extension not allowed):\\n{\\n code: \\\"invalidFiles\\\",\\n errors: [ {\\n file: the file name from the snapshot,\\n state: \\\"error\\\"|\\\"disallowed\\\"|\\\"unclean_name\\\"\\n }, ... ]\\n}\\nIf the file's error state is unclean_name, the error object will alsocontain a property cleanFile that contains the name of the file after it has been \\\"cleaned\\\" to meet our file naming requirements; for other file error states, this property is not present.\\nOn error with the project as a whole (e.g. over quota):\\n{\\n code: \\\"invalidProject\\\",\\n message: short string message for debugging\\n errors: [ array of zero or more string messages for the user ]\\n}\\nOn unexpected error (bug):\\n{\\n code: \\\"error\\\",\\n message: \\\"Unexpected Error\\\"\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex"), + new SnapshotFile( + "This text is from another file.", + "foo/bar/servermock.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + put( + 185, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\nTODO will also include updatedAt time and user (if it was not anonymous)\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\n# NB: JLM originally said PUT, but he now thinks POST is better\\n# NB: you must set a Content-Type: application/json header for this request\\n# in order to specify the data as JSON in the request body\\nPOST https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 202,\\n code: \\\"accepted\\\",\\n message: \\\"Accepted\\\"\\n}\\nResponse on out of date:\\n{\\n status: 409, # Conflict\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error with the files list (e.g. file extension not allowed):\\n{\\n code: \\\"invalidFiles\\\",\\n errors: TODO\\n}\\nOn error with the project as a whole (e.g. over quota):\\n{\\n code: \\\"invalidProject\\\",\\n errors: TODO\\n}\\nOn unexpected error (bug):\\n{\\n code: \\\"error\\\",\\n message: \\\"Unexpected Error\\\"\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex"), + new SnapshotFile( + "This text is from another file.", + "foo/bar/servermock.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + put( + 175, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\nTODO will also include updatedAt time and user (if it was not anonymous)\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\n# NB: JLM originally said PUT, but he now thinks POST is better\\n# NB: you must set a Content-Type: application/json header for this request\\n# in order to specify the data as JSON in the request body\\nPOST https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 202,\\n code: \\\"accepted\\\",\\n message: \\\"Accepted\\\"\\n}\\nResponse on out of date:\\n{\\n status: 409, # Conflict\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error:\\n{\\n code: \\\"invalidFile\\\",\\n TODO\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex"), + new SnapshotFile( + "This text is from another file.", + "foo/bar/servermock.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + put( + 146, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\n\\\\subsection{Push a Project}\\n\\n\\\\begin{lstlisting}\\nPUT https://.../api/v0/docs/1826rqgsdb/snapshots\\nData:\\n{\\n latestVerId: integer,\\n files: [\\n {\\n name: string path (forward slashes, relative to root)\\n url: string (but only if the file is modified; else no url given)\\n }, ...\\n ]\\n postbackUrl: url to post result back to\\n}\\nResponse on success:\\n{\\n status: 20x,\\n}\\nResponse on out of date:\\n{\\n status: 40x,\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\n\\nPostback Data (METHOD POST):\\nOn success:\\n{\\n code: \\\"upToDate\\\",\\n latestVerId: integer\\n}\\nOn out of date:\\n{\\n code: \\\"outOfDate\\\",\\n message: \\\"Out of Date\\\"\\n}\\nOn error:\\n{\\n code: \\\"invalidFile\\\",\\n TODO\\n}\\n\\\\end{lstlisting}\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as one of your servermock projects. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex"), + new SnapshotFile( + "This text is from another file.", + "foo/bar/servermock.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + put( + 74, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n# => { latestVerId: 39 }\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. Note that this query is not currently paginated.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n# => [\\n {\\\"versionId\\\":39,\\n \\\"comment\\\":\\\"with more files\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T18:02:19Z\\\"},\\n {\\\"versionId\\\":24,\\n \\\"comment\\\":\\\"first draft\\\",\\n \\\"user\\\":{\\n \\\"email\\\":\\\"jdleesmiller@gmail.com\\\",\\n \\\"name\\\":\\\"John Lees-Miller\\\"},\\n \\\"createdAt\\\":\\\"2014-11-05T17:56:58Z\\\"}]\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\nThe srcs array contains (content, file name) pairs; the atts array contains (URL, file name) pairs.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/39\\n# => {\\n \\\"srcs\\\":[\\n [\\\"This text is from another file.\\\",\\\"foo/bar/servermock.tex\\\"],\\n [\\\"\\\\\\\\documentclass[a4paper]{article}\\\\n...\\\",\\\"main.tex\\\"]],\\n \\\"atts\\\":[\\n [\\\"https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png\\\",\\\"min_mean_wait_evm_7_eps_150dpi.png\\\"]]}\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\nTODO still working on this part\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as a servermock project. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex"), + new SnapshotFile( + "This text is from another file.", + "foo/bar/servermock.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + put( + 39, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server, \\\\url{radiant-wind-3058.herokuapp.com}, but they're not on the production server yet.\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. To list saved versions for a doc:\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/saved_vers\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\n\\\\begin{lstlisting}\\nGET https://.../api/v0/docs/1826rqgsdb/snapshots/1\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\nTODO still working on this part\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as a servermock project. I've added an attachment and a file in a subfolder to make it a bit more interesting.\\n\\n\\\\input{foo/bar/servermock}\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex"), + new SnapshotFile( + "This text is from another file.", + "foo/bar/servermock.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + put( + 24, + new GetForVersionResult( + new SnapshotData( + Arrays.asList( + new SnapshotFile( + "\\\\documentclass[a4paper]{article}\\n\\n\\\\usepackage[english]{babel}\\n\\\\usepackage[utf8]{inputenc}\\n\\\\usepackage{graphicx}\\n\\\\usepackage{fullpage}\\n\\\\usepackage{listings}\\n\\\\usepackage{courier}\\n\\\\usepackage{url}\\n\\n\\\\lstset{basicstyle=\\\\ttfamily,breaklines=true}\\n\\n\\\\begin{document}\\n\\\\title{API for the writeLaTeX-Git Bridge}\\n\\\\author{JLM}\\n\\\\date{\\\\today}\\n\\\\maketitle\\n\\n\\\\section{Fetching a Project from WriteLaTeX}\\n\\nThere are three API calls that will likely be of interest. You can run them against this server (radiant-wind-3058.herokuapp.com).\\n\\n\\\\subsection{Get Doc}\\n\\nA ``doc'' is our internal term for a ``project''. At present, this just returns the latest version number.\\n\\n\\\\begin{lstlisting}\\nGET https://radiant-wind.....com/api/v0/docs/1826rqgsdb\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Saved Vers}\\n\\nA ``saved ver'' is a version of a doc, saved by via the versions menu. To list saved versions for a doc:\\n\\n\\\\begin{lstlisting}\\nGET https://radiant-wind.....com/api/v0/docs/1826rqgsdb/saved_vers\\n\\\\end{lstlisting}\\n\\n\\\\subsection{Get Snapshot for Version}\\n\\nA snapshot contains the content of a project in the given version. You can safely request a snapshot of any version that is, or was at any point in the last 24 hours, (1) a saved version, or (2) the current version. (Older versions may or may not have been moved to cold storage.)\\n\\n\\\\begin{lstlisting}\\nGET https://radiant-wind.....com/api/v0/docs/1826rqgsdb/snapshots/1\\n\\\\end{lstlisting}\\n\\n\\\\section{Pushing a Project to WriteLaTeX}\\n\\nTODO still working on this part\\n\\n\\\\section{Test Data}\\n\\nYou can use this project as a servermock project. Here is an extra file to make it a bit more interesting.\\n\\n\\\\includegraphics[width=\\\\linewidth]{min_mean_wait_evm_7_eps_150dpi}\\n\\n\\\\end{document}", + "main.tex")), + Arrays.asList( + new SnapshotAttachment( + "https://writelatex-staging.s3.amazonaws.com/filepicker/1ENnu6zJSGyslI3DuNZD_min_mean_wait_evm_7.eps.150dpi.png", + "min_mean_wait_evm_7_eps_150dpi.png"))))); + } + }); + } + }; - public GetDocResult getStateForGetDoc(String projectName) { - return getDoc.get(projectName); - } + push = + new HashMap() { + { + put("1826rqgsdb", new SnapshotPushResultSuccess()); + } + }; - public GetSavedVersResult getStateForGetSavedVers(String projectName) { - return getSavedVers.get(projectName); - } + postback = + new HashMap() { + { + // put( + // "1826rqgsdb", + // new SnapshotPostbackRequestInvalidFiles( + // Arrays.asList( + // new InvalidFileErrorDefault( + // "file1.invalid" + // ), + // new InvalidFileErrorDisallowed( + // "file2.exe" + // ), + // new InvalidFileErrorUnclean( + // "hello world.png", + // "hello_world.png" + // ) + // ) + // ) + // ); + // put("1826rqgsdb", new SnapshotPostbackRequestOutOfDate()); + put( + "1826rqgsdb", + new SnapshotPostbackRequestInvalidProject( + Arrays.asList( + "Your project is missing main.tex.", + "Please name your main latex file main.tex."))); + // put("1826rqgsdb", new SnapshotPostbackRequestError()); + } + }; + } - public GetForVersionResult getStateForGetForVers( - String projectName, - int versionID - ) { - return getForVers.get(projectName).get(versionID); - } + public GetDocResult getStateForGetDoc(String projectName) { + return getDoc.get(projectName); + } - public SnapshotPushResult getStateForPush(String projectName) { - return push.get(projectName); - } + public GetSavedVersResult getStateForGetSavedVers(String projectName) { + return getSavedVers.get(projectName); + } - public SnapshotPostbackRequest getStateForPostback(String projectName) { - return postback.get(projectName); - } + public GetForVersionResult getStateForGetForVers(String projectName, int versionID) { + return getForVers.get(projectName).get(versionID); + } + public SnapshotPushResult getStateForPush(String projectName) { + return push.get(projectName); + } + + public SnapshotPostbackRequest getStateForPostback(String projectName) { + return postback.get(projectName); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIStateBuilder.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIStateBuilder.java index 99f13239f4..d5dcd1bcca 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIStateBuilder.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/state/SnapshotAPIStateBuilder.java @@ -4,6 +4,9 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.*; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; import uk.ac.ic.wlgitbridge.snapshot.getforversion.GetForVersionResult; import uk.ac.ic.wlgitbridge.snapshot.getforversion.SnapshotAttachment; @@ -16,213 +19,142 @@ import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data.SnapshotPushR import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.data.SnapshotPushResultSuccess; import uk.ac.ic.wlgitbridge.snapshot.servermock.response.push.postback.*; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.*; - -/** +/* * Created by Winston on 11/01/15. */ public class SnapshotAPIStateBuilder { - private final JsonArray projects; + private final JsonArray projects; - private Map getDoc = new HashMap<>(); - private Map getSavedVers = new HashMap<>(); - private Map> getForVers - = new HashMap<>(); - private Map push - = new HashMap<>(); - private Map postback - = new HashMap<>(); + private Map getDoc = new HashMap<>(); + private Map getSavedVers = new HashMap<>(); + private Map> getForVers = new HashMap<>(); + private Map push = new HashMap<>(); + private Map postback = new HashMap<>(); - public SnapshotAPIStateBuilder(InputStream stream) { - projects = new Gson().fromJson( - new InputStreamReader(stream), JsonArray.class - ); + public SnapshotAPIStateBuilder(InputStream stream) { + projects = new Gson().fromJson(new InputStreamReader(stream), JsonArray.class); + } + + public SnapshotAPIState build() { + for (JsonElement project : projects) { + addProject(project.getAsJsonObject()); } + return new SnapshotAPIState(getDoc, getSavedVers, getForVers, push, postback); + } - public SnapshotAPIState build() { - for (JsonElement project : projects) { - addProject(project.getAsJsonObject()); - } - return new SnapshotAPIState( - getDoc, - getSavedVers, - getForVers, - push, - postback - ); + private void addProject(JsonObject project) { + String projectName = project.get("project").getAsString(); + addGetDocForProject(projectName, project.get("getDoc").getAsJsonObject()); + addGetSavedVersForProject(projectName, project.get("getSavedVers").getAsJsonArray()); + addGetForVersForProject(projectName, project.get("getForVers").getAsJsonArray()); + addPushForProject(projectName, project.get("push").getAsString()); + addPostbackForProject(projectName, project.get("postback").getAsJsonObject()); + } + + private void addGetDocForProject(String projectName, JsonObject jsonGetDoc) { + int versionID = jsonGetDoc.get("versionID").getAsInt(); + String createdAt = null; + String email = null; + String name = null; + if (jsonGetDoc.has("createdAt")) { + createdAt = jsonGetDoc.get("createdAt").getAsString(); } - - private void addProject(JsonObject project) { - String projectName = project.get("project").getAsString(); - addGetDocForProject( - projectName, - project.get("getDoc").getAsJsonObject() - ); - addGetSavedVersForProject( - projectName, - project.get("getSavedVers").getAsJsonArray() - ); - addGetForVersForProject( - projectName, - project.get("getForVers").getAsJsonArray() - ); - addPushForProject( - projectName, - project.get("push").getAsString() - ); - addPostbackForProject( - projectName, - project.get("postback").getAsJsonObject() - ); + if (jsonGetDoc.has("email")) { + email = jsonGetDoc.get("email").getAsString(); } - - private void addGetDocForProject( - String projectName, - JsonObject jsonGetDoc - ) { - int versionID = jsonGetDoc.get("versionID").getAsInt(); - String createdAt = null; - String email = null; - String name = null; - if (jsonGetDoc.has("createdAt")) { - createdAt = jsonGetDoc.get("createdAt").getAsString(); - } - if (jsonGetDoc.has("email")) { - email = jsonGetDoc.get("email").getAsString(); - } - if (jsonGetDoc.has("name")) { - name = jsonGetDoc.get("name").getAsString(); - } - getDoc.put( - projectName, - new GetDocResult( - jsonGetDoc.get("error"), - versionID, - createdAt, - email, - name - ) - ); + if (jsonGetDoc.has("name")) { + name = jsonGetDoc.get("name").getAsString(); } + getDoc.put( + projectName, new GetDocResult(jsonGetDoc.get("error"), versionID, createdAt, email, name)); + } - private void addGetSavedVersForProject( - String projectName, - JsonArray jsonGetSavedVers - ) { - List savedVers = new ArrayList<>(); - for (JsonElement ver : jsonGetSavedVers) { - savedVers.add(getSnapshotInfo(ver.getAsJsonObject())); - } - getSavedVers.put(projectName, new GetSavedVersResult(savedVers)); + private void addGetSavedVersForProject(String projectName, JsonArray jsonGetSavedVers) { + List savedVers = new ArrayList<>(); + for (JsonElement ver : jsonGetSavedVers) { + savedVers.add(getSnapshotInfo(ver.getAsJsonObject())); } + getSavedVers.put(projectName, new GetSavedVersResult(savedVers)); + } - private SnapshotInfo getSnapshotInfo( - JsonObject jsonSnapshotInfo - ) { - return new SnapshotInfo( - jsonSnapshotInfo.get("versionID").getAsInt(), - jsonSnapshotInfo.get("comment").getAsString(), - jsonSnapshotInfo.get("email").getAsString(), - jsonSnapshotInfo.get("name").getAsString(), - jsonSnapshotInfo.get("createdAt").getAsString() - ); + private SnapshotInfo getSnapshotInfo(JsonObject jsonSnapshotInfo) { + return new SnapshotInfo( + jsonSnapshotInfo.get("versionID").getAsInt(), + jsonSnapshotInfo.get("comment").getAsString(), + jsonSnapshotInfo.get("email").getAsString(), + jsonSnapshotInfo.get("name").getAsString(), + jsonSnapshotInfo.get("createdAt").getAsString()); + } + + private void addGetForVersForProject(String projectName, JsonArray jsonGetForVers) { + Map forVers = new HashMap<>(); + for (JsonElement forVer : jsonGetForVers) { + JsonObject forVerObj = forVer.getAsJsonObject(); + forVers.put( + forVerObj.get("versionID").getAsInt(), + new GetForVersionResult( + new SnapshotData( + getSrcs(forVerObj.get("srcs").getAsJsonArray()), + getAtts(forVerObj.get("atts").getAsJsonArray())))); } + getForVers.put(projectName, forVers); + } - private void addGetForVersForProject( - String projectName, - JsonArray jsonGetForVers - ) { - Map forVers = new HashMap<>(); - for (JsonElement forVer : jsonGetForVers) { - JsonObject forVerObj = forVer.getAsJsonObject(); - forVers.put( - forVerObj.get("versionID").getAsInt(), - new GetForVersionResult( - new SnapshotData( - getSrcs( - forVerObj.get( - "srcs" - ).getAsJsonArray() - ), - getAtts( - forVerObj.get( - "atts" - ).getAsJsonArray() - ) - ) - ) - ); - } - getForVers.put(projectName, forVers); + private List getSrcs(JsonArray jsonSrcs) { + List srcs = new ArrayList<>(); + for (JsonElement src : jsonSrcs) { + srcs.add(getSrc(src.getAsJsonObject())); } + return srcs; + } - private List getSrcs(JsonArray jsonSrcs) { - List srcs = new ArrayList<>(); - for (JsonElement src : jsonSrcs) { - srcs.add(getSrc(src.getAsJsonObject())); - } - return srcs; + private SnapshotFile getSrc(JsonObject jsonSrc) { + return new SnapshotFile( + jsonSrc.get("content").getAsString(), jsonSrc.get("path").getAsString()); + } + + private List getAtts(JsonArray jsonAtts) { + List atts = new LinkedList<>(); + for (JsonElement att : jsonAtts) { + atts.add(getAtt(att.getAsJsonObject())); } + return atts; + } - private SnapshotFile getSrc(JsonObject jsonSrc) { - return new SnapshotFile(jsonSrc.get("content").getAsString(), - jsonSrc.get("path").getAsString()); + private SnapshotAttachment getAtt(JsonObject jsonAtt) { + return new SnapshotAttachment( + jsonAtt.get("url").getAsString(), jsonAtt.get("path").getAsString()); + } + + private void addPushForProject(String projectName, String jsonPush) { + SnapshotPushResult p; + if (jsonPush.equals("success")) { + p = new SnapshotPushResultSuccess(); + } else if (jsonPush.equals("outOfDate")) { + p = new SnapshotPushResultOutOfDate(); + } else { + throw new IllegalArgumentException("invalid push"); } + push.put(projectName, p); + } - private List getAtts(JsonArray jsonAtts) { - List atts = new LinkedList<>(); - for (JsonElement att : jsonAtts) { - atts.add(getAtt(att.getAsJsonObject())); - } - return atts; + private void addPostbackForProject(String projectName, JsonObject jsonPostback) { + SnapshotPostbackRequest p; + String type = jsonPostback.get("type").getAsString(); + if (type.equals("success")) { + p = new SnapshotPostbackRequestSuccess(jsonPostback.get("versionID").getAsInt()); + } else if (type.equals("outOfDate")) { + p = new SnapshotPostbackRequestOutOfDate(); + } else if (type.equals("invalidFiles")) { + p = new SnapshotPostbackRequestInvalidFiles(jsonPostback.get("errors").getAsJsonArray()); + } else if (type.equals("invalidProject")) { + p = new SnapshotPostbackRequestInvalidProject(jsonPostback.get("errors").getAsJsonArray()); + } else if (type.equals("error")) { + p = new SnapshotPostbackRequestError(); + } else { + throw new IllegalArgumentException("invalid postback type"); } - - private SnapshotAttachment getAtt(JsonObject jsonAtt) { - return new SnapshotAttachment(jsonAtt.get("url").getAsString(), - jsonAtt.get("path").getAsString()); - } - - private void addPushForProject(String projectName, String jsonPush) { - SnapshotPushResult p; - if (jsonPush.equals("success")) { - p = new SnapshotPushResultSuccess(); - } else if (jsonPush.equals("outOfDate")) { - p = new SnapshotPushResultOutOfDate(); - } else { - throw new IllegalArgumentException("invalid push"); - } - push.put(projectName, p); - } - - private void addPostbackForProject( - String projectName, - JsonObject jsonPostback - ) { - SnapshotPostbackRequest p; - String type = jsonPostback.get("type").getAsString(); - if (type.equals("success")) { - p = new SnapshotPostbackRequestSuccess( - jsonPostback.get("versionID").getAsInt() - ); - } else if (type.equals("outOfDate")) { - p = new SnapshotPostbackRequestOutOfDate(); - } else if (type.equals("invalidFiles")) { - p = new SnapshotPostbackRequestInvalidFiles( - jsonPostback.get("errors").getAsJsonArray() - ); - } else if (type.equals("invalidProject")) { - p = new SnapshotPostbackRequestInvalidProject( - jsonPostback.get("errors").getAsJsonArray() - ); - } else if (type.equals("error")) { - p = new SnapshotPostbackRequestError(); - } else { - throw new IllegalArgumentException("invalid postback type"); - } - postback.put(projectName, p); - } - + postback.put(projectName, p); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtil.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtil.java index 2e798b03d5..380c005589 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtil.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtil.java @@ -1,12 +1,6 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.util; import com.google.common.collect.ImmutableSet; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.api.errors.NoHeadException; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.storage.file.FileRepositoryBuilder; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -14,178 +8,135 @@ import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.NoHeadException; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; -/** +/* * Created by Winston on 11/01/15. */ public class FileUtil { - public static boolean currentCommitsAreEqual(Path dir1, Path dir2) { - try { - RevCommit commit1 = new Git( - new FileRepositoryBuilder().setWorkTree( - dir1.toFile().getAbsoluteFile() - ).build() - ).log().call().iterator().next(); - RevCommit commit2 = new Git( - new FileRepositoryBuilder().setWorkTree( - dir2.toFile().getAbsoluteFile() - ).build() - ).log().call().iterator().next(); - return commit1.equals(commit2); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (NoHeadException e) { - return false; - } catch (GitAPIException e) { - throw new RuntimeException(e); - } + public static boolean currentCommitsAreEqual(Path dir1, Path dir2) { + try { + RevCommit commit1 = + new Git(new FileRepositoryBuilder().setWorkTree(dir1.toFile().getAbsoluteFile()).build()) + .log() + .call() + .iterator() + .next(); + RevCommit commit2 = + new Git(new FileRepositoryBuilder().setWorkTree(dir2.toFile().getAbsoluteFile()).build()) + .log() + .call() + .iterator() + .next(); + return commit1.equals(commit2); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (NoHeadException e) { + return false; + } catch (GitAPIException e) { + throw new RuntimeException(e); } + } - public static boolean gitDirectoriesAreEqual(Path dir1, Path dir2) { - Set dir1Contents = getAllRecursivelyInDirectoryApartFrom( - dir1, - dir1.resolve(".git") - ); - Set dir2Contents = getAllRecursivelyInDirectoryApartFrom( - dir2, - dir2.resolve(".git") - ); - return filesAreEqual(dir1, dir2, dir1Contents, dir2Contents); + public static boolean gitDirectoriesAreEqual(Path dir1, Path dir2) { + Set dir1Contents = getAllRecursivelyInDirectoryApartFrom(dir1, dir1.resolve(".git")); + Set dir2Contents = getAllRecursivelyInDirectoryApartFrom(dir2, dir2.resolve(".git")); + return filesAreEqual(dir1, dir2, dir1Contents, dir2Contents); + } + + public static boolean directoryDeepEquals(File dir, File dir_) { + return directoryDeepEquals(dir.toPath(), dir_.toPath()); + } + + public static boolean directoryDeepEquals(Path path, Path path_) { + List> contents = + Stream.of(path, path_) + .map(p -> getAllFilesRecursively(p, p, Collections.emptySet(), true)) + .collect(Collectors.toList()); + return filesAreEqual(path, path_, contents.get(0), contents.get(1)); + } + + private static boolean filesAreEqual( + Path dir1, Path dir2, Set dir1Contents, Set dir2Contents) { + boolean filesEqual = dir1Contents.equals(dir2Contents); + if (!filesEqual) { + System.out.println("Not equal: (" + dir1Contents + ", " + dir2Contents + ")"); + System.out.println(dir1 + ": " + dir1Contents); + System.out.println(dir2 + ": " + dir2Contents); } + return filesEqual && directoryContentsEqual(dir1Contents, dir1, dir2); + } - public static boolean directoryDeepEquals(File dir, File dir_) { - return directoryDeepEquals(dir.toPath(), dir_.toPath()); + static boolean directoryContentsEqual(Set dirContents, Path dir1, Path dir2) { + for (String file : dirContents) { + Path path1 = dir1.resolve(file); + Path path2 = dir2.resolve(file); + if (!path1.toFile().isDirectory() + && !path2.toFile().isDirectory() + && !fileContentsEqual(path1, path2)) { + return false; + } } + return true; + } - public static boolean directoryDeepEquals(Path path, Path path_) { - List> contents = Stream.of(path, path_).map(p -> - getAllFilesRecursively( - p, p, Collections.emptySet(), true - ) - ).collect(Collectors.toList()); - return filesAreEqual(path, path_, contents.get(0), contents.get(1)); + private static boolean fileContentsEqual(Path first, Path second) { + try { + byte[] firstContents = Files.readAllBytes(first); + byte[] secondContents = Files.readAllBytes(second); + boolean equals = Arrays.equals(firstContents, secondContents); + if (!equals) { + System.out.println("Not equal: (" + first + ", " + second + ")"); + System.out.println(first + ": " + new String(firstContents)); + System.out.println(second + ": " + new String(secondContents)); + } + return equals; + } catch (IOException e) { + throw new RuntimeException(e); } + } - private static boolean filesAreEqual( - Path dir1, Path dir2, - Set dir1Contents, Set dir2Contents - ) { - boolean filesEqual = dir1Contents.equals(dir2Contents); - if (!filesEqual) { - System.out.println( - "Not equal: (" - + dir1Contents - + ", " - + dir2Contents - + ")" - ); - System.out.println(dir1 + ": " + dir1Contents); - System.out.println(dir2 + ": " + dir2Contents); - } - return filesEqual && directoryContentsEqual(dir1Contents, dir1, dir2); + public static Set getAllRecursivelyInDirectoryApartFrom(Path dir, Path excluded) { + return getAllRecursivelyInDirectoryApartFrom(dir, excluded, true); + } + + public static Set getOnlyFilesRecursivelyInDirectoryApartFrom(Path dir, Path excluded) { + return getAllRecursivelyInDirectoryApartFrom(dir, excluded, false); + } + + private static Set getAllRecursivelyInDirectoryApartFrom( + Path dir, Path excluded, boolean directories) { + if (!dir.toFile().isDirectory()) { + throw new IllegalArgumentException("need a directory"); } + return getAllFilesRecursively(dir, dir, ImmutableSet.of(excluded.toFile()), directories); + } - static boolean directoryContentsEqual( - Set dirContents, - Path dir1, - Path dir2 - ) { - for (String file : dirContents) { - Path path1 = dir1.resolve(file); - Path path2 = dir2.resolve(file); - if ( - !path1.toFile().isDirectory() - && !path2.toFile().isDirectory() - && !fileContentsEqual(path1, path2) - ) { - return false; - } - } - return true; + private static final Set ExcludedNames = ImmutableSet.of(".DS_Store"); + + static Set getAllFilesRecursively( + Path baseDir, Path dir, Set excluded, boolean directories) { + Set files = new HashSet(); + for (File file : dir.toFile().listFiles()) { + if (excluded.contains(file)) { + continue; + } + if (ExcludedNames.contains(file.getName())) { + continue; + } + boolean isDirectory = file.isDirectory(); + if (directories || !isDirectory) { + files.add(baseDir.relativize(file.toPath()).toString()); + } + if (isDirectory) { + files.addAll(getAllFilesRecursively(baseDir, file.toPath(), excluded, directories)); + } } - - private static boolean fileContentsEqual(Path first, Path second) { - try { - byte[] firstContents = Files.readAllBytes(first); - byte[] secondContents = Files.readAllBytes(second); - boolean equals = Arrays.equals(firstContents, secondContents); - if (!equals) { - System.out.println( - "Not equal: (" + first + ", " + second + ")" - ); - System.out.println(first + ": " + new String(firstContents)); - System.out.println(second + ": " + new String(secondContents)); - } - return equals; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static Set getAllRecursivelyInDirectoryApartFrom( - Path dir, - Path excluded - ) { - return getAllRecursivelyInDirectoryApartFrom( - dir, excluded, true - ); - } - - public static Set getOnlyFilesRecursivelyInDirectoryApartFrom( - Path dir, - Path excluded - ) { - return getAllRecursivelyInDirectoryApartFrom( - dir, excluded, false - ); - } - - private static Set getAllRecursivelyInDirectoryApartFrom( - Path dir, - Path excluded, - boolean directories - ) { - if (!dir.toFile().isDirectory()) { - throw new IllegalArgumentException("need a directory"); - } - return getAllFilesRecursively( - dir, dir, ImmutableSet.of(excluded.toFile()), directories - ); - } - - private static final Set ExcludedNames = ImmutableSet.of( - ".DS_Store" - ); - - static Set getAllFilesRecursively( - Path baseDir, - Path dir, - Set excluded, - boolean directories - ) { - Set files = new HashSet(); - for (File file : dir.toFile().listFiles()) { - if (excluded.contains(file)) { - continue; - } - if (ExcludedNames.contains(file.getName())) { - continue; - } - boolean isDirectory = file.isDirectory(); - if (directories || !isDirectory) { - files.add(baseDir.relativize(file.toPath()).toString()); - } - if (isDirectory) { - files.addAll(getAllFilesRecursively( - baseDir, - file.toPath(), - excluded, - directories - )); - } - } - return files; - } - + return files; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/BiConsumerT.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/BiConsumerT.java index dae03e46ff..f1384ea492 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/BiConsumerT.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/BiConsumerT.java @@ -1,11 +1,10 @@ package uk.ac.ic.wlgitbridge.util; -/** +/* * BiConsumer interface that allows checked exceptions. */ @FunctionalInterface public interface BiConsumerT { - void accept(T t, U u) throws E; - + void accept(T t, U u) throws E; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/DeletingFileInputStream.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/DeletingFileInputStream.java index 5ba4832979..6589a42110 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/DeletingFileInputStream.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/DeletingFileInputStream.java @@ -2,59 +2,59 @@ package uk.ac.ic.wlgitbridge.util; import java.io.*; -/** +/* * A {@link java.io.FileInputStream} which deletes the underlying * {@link java.io.File} on close. * * @author Michael Walker (barrucadu) {@literal } */ public class DeletingFileInputStream extends FileInputStream { - private File file; + private File file; - /** - * Creates a {@link java.io.FileInputStream} by opening a - * connection to an actual file, the file named by the - * {@link java.io.File} object file in the file system. - * - * When the {@link close} method is called, the {@code File} will - * be deleted. - */ - public DeletingFileInputStream(File file) throws FileNotFoundException { - super(file); - this.file = file; + /* + * Creates a {@link java.io.FileInputStream} by opening a + * connection to an actual file, the file named by the + * {@link java.io.File} object file in the file system. + * + * When the {@link close} method is called, the {@code File} will + * be deleted. + */ + public DeletingFileInputStream(File file) throws FileNotFoundException { + super(file); + this.file = file; + } + + /* + * Closes this input stream and deletes the underlying file. + */ + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + if (file != null) { + file.delete(); + file = null; + } } + } - /** - * Closes this input stream and deletes the underlying file. - */ - @Override - public void close() throws IOException { + /* + * We shouldn't rely on this for correctness! + */ + @Override + protected void finalize() throws IOException { + try { + super.finalize(); + } finally { + if (file != null) { + Log.warn("File open at finalization time: {}", file.getCanonicalPath()); try { - super.close(); - } finally { - if(file != null) { - file.delete(); - file = null; - } - } - } - - /** - * We shouldn't rely on this for correctness! - */ - @Override - protected void finalize() throws IOException { - try { - super.finalize(); - } finally { - if(file != null) { - Log.warn("File open at finalization time: {}", file.getCanonicalPath()); - try { - close(); - } catch (IOException e) { - Log.error("Failed to delete file", e); - } - } + close(); + } catch (IOException e) { + Log.error("Failed to delete file", e); } + } } + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Files.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Files.java index d5f20a0f97..bd41161329 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Files.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Files.java @@ -1,9 +1,6 @@ package uk.ac.ic.wlgitbridge.util; import com.google.api.client.repackaged.com.google.common.base.Preconditions; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.TrueFileFilter; - import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -12,119 +9,90 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; -/** +/* * Created by winston on 23/08/2016. */ public class Files { - private Files() {} + private Files() {} - public static boolean contentsAreEqual( - File f0, - File f1 - ) throws IOException { - try { - return uncheckedContentsAreEqual(f0, f1); - } catch (UncheckedIOException e) { - throw e.getCause(); - } + public static boolean contentsAreEqual(File f0, File f1) throws IOException { + try { + return uncheckedContentsAreEqual(f0, f1); + } catch (UncheckedIOException e) { + throw e.getCause(); } + } - public static void renameAll( - File fileOrDir, - String from, - String to - ) { - if (fileOrDir.isDirectory()) { - File f = doRename(fileOrDir, from, to); - for (File c : f.listFiles()) { - renameAll(c, from, to); - } - } else if (fileOrDir.isFile()) { - doRename(fileOrDir, from, to); - } else { - throw new IllegalArgumentException( - "not a file or dir: " + fileOrDir - ); - } + public static void renameAll(File fileOrDir, String from, String to) { + if (fileOrDir.isDirectory()) { + File f = doRename(fileOrDir, from, to); + for (File c : f.listFiles()) { + renameAll(c, from, to); + } + } else if (fileOrDir.isFile()) { + doRename(fileOrDir, from, to); + } else { + throw new IllegalArgumentException("not a file or dir: " + fileOrDir); } + } - private static File doRename(File fileOrDir, String from, String to) { - if (!fileOrDir.getName().equals(from)) { - return fileOrDir; - } - File renamed = new File(fileOrDir.getParent(), to); - Preconditions.checkState(fileOrDir.renameTo(renamed)); - return renamed; + private static File doRename(File fileOrDir, String from, String to) { + if (!fileOrDir.getName().equals(from)) { + return fileOrDir; } + File renamed = new File(fileOrDir.getParent(), to); + Preconditions.checkState(fileOrDir.renameTo(renamed)); + return renamed; + } - private static boolean uncheckedContentsAreEqual( - File f0, - File f1 - ) throws IOException { - if (f0.equals(f1)) { - return true; - } - if (!f0.isDirectory() || !f1.isDirectory()) { - return !f0.isDirectory() && !f1.isDirectory() && - Arrays.equals( - FileUtils.readFileToByteArray(f0), - FileUtils.readFileToByteArray(f1) - ); - } - Path f0Base = Paths.get(f0.getAbsolutePath()); - Path f1Base = Paths.get(f1.getAbsolutePath()); - Set children0 = getChildren(f0, f0Base); - Set children1 = getChildren(f1, f1Base); - if (children0.size() != children1.size()) { - return false; - } - return children0.stream( - ).allMatch(c0 -> - children1.contains(c0) && childEquals(c0, f0Base, f1Base) - ); + private static boolean uncheckedContentsAreEqual(File f0, File f1) throws IOException { + if (f0.equals(f1)) { + return true; } - - private static Set getChildren(File f0, Path f0Base) { - return FileUtils.listFilesAndDirs( - f0, - TrueFileFilter.TRUE, - TrueFileFilter.TRUE - ).stream( - ).map( - File::getAbsolutePath - ).map( - Paths::get - ).map(p -> - f0Base.relativize(p) - ).filter(p -> - !p.toString().isEmpty() - ).collect( - Collectors.toSet() - ); + if (!f0.isDirectory() || !f1.isDirectory()) { + return !f0.isDirectory() + && !f1.isDirectory() + && Arrays.equals(FileUtils.readFileToByteArray(f0), FileUtils.readFileToByteArray(f1)); } - - private static boolean childEquals( - Path child, - Path f0Base, - Path f1Base - ) throws UncheckedIOException { - File c0 = f0Base.resolve(child).toFile(); - File c1 = f1Base.resolve(child).toFile(); - boolean c0IsDir = c0.isDirectory(); - boolean c1IsDir = c1.isDirectory(); - if (c0IsDir || c1IsDir) { - return c0IsDir && c1IsDir; - } - try { - return c0.isFile() && c1.isFile() && Arrays.equals( - FileUtils.readFileToByteArray(c0), - FileUtils.readFileToByteArray(c1) - ); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + Path f0Base = Paths.get(f0.getAbsolutePath()); + Path f1Base = Paths.get(f1.getAbsolutePath()); + Set children0 = getChildren(f0, f0Base); + Set children1 = getChildren(f1, f1Base); + if (children0.size() != children1.size()) { + return false; } + return children0.stream() + .allMatch(c0 -> children1.contains(c0) && childEquals(c0, f0Base, f1Base)); + } + private static Set getChildren(File f0, Path f0Base) { + return FileUtils.listFilesAndDirs(f0, TrueFileFilter.TRUE, TrueFileFilter.TRUE).stream() + .map(File::getAbsolutePath) + .map(Paths::get) + .map(p -> f0Base.relativize(p)) + .filter(p -> !p.toString().isEmpty()) + .collect(Collectors.toSet()); + } + + private static boolean childEquals(Path child, Path f0Base, Path f1Base) + throws UncheckedIOException { + File c0 = f0Base.resolve(child).toFile(); + File c1 = f1Base.resolve(child).toFile(); + boolean c0IsDir = c0.isDirectory(); + boolean c1IsDir = c1.isDirectory(); + if (c0IsDir || c1IsDir) { + return c0IsDir && c1IsDir; + } + try { + return c0.isFile() + && c1.isFile() + && Arrays.equals(FileUtils.readFileToByteArray(c0), FileUtils.readFileToByteArray(c1)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/FunctionT.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/FunctionT.java index 8920830f46..43d58dd154 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/FunctionT.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/FunctionT.java @@ -1,6 +1,6 @@ package uk.ac.ic.wlgitbridge.util; -/** +/* * Function interface that allows checked exceptions. * @param * @param @@ -9,6 +9,5 @@ package uk.ac.ic.wlgitbridge.util; @FunctionalInterface public interface FunctionT { - R apply(T t) throws E; - + R apply(T t) throws E; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Instance.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Instance.java index 75e72924aa..1b55416c56 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Instance.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Instance.java @@ -8,25 +8,19 @@ import com.google.api.client.json.gson.GsonFactory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -/** +/* * Created by winston on 25/10/15. */ public class Instance { - public static final HttpTransport httpTransport = - new NetHttpTransport(); + public static final HttpTransport httpTransport = new NetHttpTransport(); - public static final HttpRequestFactory httpRequestFactory = - httpTransport.createRequestFactory(); + public static final HttpRequestFactory httpRequestFactory = httpTransport.createRequestFactory(); - public static final JsonFactory jsonFactory = new GsonFactory(); + public static final JsonFactory jsonFactory = new GsonFactory(); - public static final Gson prettyGson = new GsonBuilder() - .setPrettyPrinting() - .serializeNulls() - .disableHtmlEscaping() - .create(); - - public static final Gson gson = new Gson(); + public static final Gson prettyGson = + new GsonBuilder().setPrettyPrinting().serializeNulls().disableHtmlEscaping().create(); + public static final Gson gson = new Gson(); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Log.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Log.java index 10d9e55d62..92b7d94197 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Log.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Log.java @@ -4,83 +4,82 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.ic.wlgitbridge.application.GitBridgeApp; -/** +/* * Created by winston on 19/01/2016. */ public class Log { - private static Logger logger = LoggerFactory.getLogger(GitBridgeApp.class); + private static Logger logger = LoggerFactory.getLogger(GitBridgeApp.class); - public static void trace(String msg) { - logger.trace(msg); - } + public static void trace(String msg) { + logger.trace(msg); + } - public static void trace(String msg, Throwable t) { - logger.trace(msg, t); - } + public static void trace(String msg, Throwable t) { + logger.trace(msg, t); + } - public static void debug(String msg) { - logger.debug(msg); - } + public static void debug(String msg) { + logger.debug(msg); + } - public static void debug(String msg, Throwable t) { - logger.debug(msg, t); - } + public static void debug(String msg, Throwable t) { + logger.debug(msg, t); + } - public static void debug(String format, Object... args) { - logger.debug(format, args); - } + public static void debug(String format, Object... args) { + logger.debug(format, args); + } - public static void info(String msg) { - logger.info(msg); - } + public static void info(String msg) { + logger.info(msg); + } - public static void info(String format, Object arg) { - logger.info(format, arg); - } + public static void info(String format, Object arg) { + logger.info(format, arg); + } - public static void info(String format, Object arg1, Object arg2) { - logger.info(format, arg1, arg2); - } + public static void info(String format, Object arg1, Object arg2) { + logger.info(format, arg1, arg2); + } - public static void info(String format, Object... args) { - logger.info(format, args); - } + public static void info(String format, Object... args) { + logger.info(format, args); + } - public static void info(String msg, Throwable t) { - logger.info(msg, t); - } + public static void info(String msg, Throwable t) { + logger.info(msg, t); + } - public static void warn(String msg) { - logger.warn(msg); - } + public static void warn(String msg) { + logger.warn(msg); + } - public static void warn(String msg, Object arg) { - logger.warn(msg, arg); - } + public static void warn(String msg, Object arg) { + logger.warn(msg, arg); + } - public static void warn(String msg, Object arg1, Object arg2) { - logger.warn(msg, arg1, arg2); - } + public static void warn(String msg, Object arg1, Object arg2) { + logger.warn(msg, arg1, arg2); + } - public static void warn(String msg, Object... args) { - logger.warn(msg, args); - } + public static void warn(String msg, Object... args) { + logger.warn(msg, args); + } - public static void warn(String msg, Throwable t) { - logger.warn(msg, t); - } + public static void warn(String msg, Throwable t) { + logger.warn(msg, t); + } - public static void error(String msg) { - logger.error(msg); - } + public static void error(String msg) { + logger.error(msg); + } - public static void error(String msg, Object... args) { - logger.error(msg, args); - } - - public static void error(String msg, Throwable t) { - logger.error(msg, t); - } + public static void error(String msg, Object... args) { + logger.error(msg, args); + } + public static void error(String msg, Throwable t) { + logger.error(msg, t); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Project.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Project.java index 4e091e2489..a6e0fa4f93 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Project.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Project.java @@ -2,19 +2,17 @@ package uk.ac.ic.wlgitbridge.util; import com.google.common.base.Preconditions; -/** +/* * Created by winston on 23/08/2016. */ public class Project { - public static boolean isValidProjectName(String projectName) { - return projectName != null && !projectName.isEmpty() - && !projectName.startsWith("."); - } - - public static void checkValidProjectName(String projectName) { - Preconditions.checkArgument(isValidProjectName(projectName), - "[%s] invalid project name", projectName); - } + public static boolean isValidProjectName(String projectName) { + return projectName != null && !projectName.isEmpty() && !projectName.startsWith("."); + } + public static void checkValidProjectName(String projectName) { + Preconditions.checkArgument( + isValidProjectName(projectName), "[%s] invalid project name", projectName); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/ResourceUtil.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/ResourceUtil.java index 57c22287b8..6899541e3e 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/ResourceUtil.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/ResourceUtil.java @@ -1,32 +1,27 @@ package uk.ac.ic.wlgitbridge.util; -import org.apache.commons.io.FileUtils; - import java.io.File; import java.io.IOException; +import org.apache.commons.io.FileUtils; public class ResourceUtil { - /** - * Creates a copy of a resource folder. Mainly used for testing to prevent - * the original folder from being mangled. - * - * It will have the same name as the original. - * @param resource the resource name, e.g. "/uk/ac/ic/wlgitbridge/file.txt" - * @param folderProvider function used to create the folder. - * E.g. TemporaryFolder from junit - * @return - * @throws IOException - */ - public static File copyOfFolderResource( - String resource, - FunctionT folderProvider - ) throws IOException { - File original - = new File(ResourceUtil.class.getResource(resource).getFile()); - File tmp = folderProvider.apply(original.getName()); - FileUtils.copyDirectory(original, tmp); - return tmp; - } - + /* + * Creates a copy of a resource folder. Mainly used for testing to prevent + * the original folder from being mangled. + * + * It will have the same name as the original. + * @param resource the resource name, e.g. "/uk/ac/ic/wlgitbridge/file.txt" + * @param folderProvider function used to create the folder. + * E.g. TemporaryFolder from junit + * @return + * @throws IOException + */ + public static File copyOfFolderResource( + String resource, FunctionT folderProvider) throws IOException { + File original = new File(ResourceUtil.class.getResource(resource).getFile()); + File tmp = folderProvider.apply(original.getName()); + FileUtils.copyDirectory(original, tmp); + return tmp; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Tar.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Tar.java index 6de56e7acf..878adde27d 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Tar.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Tar.java @@ -1,6 +1,9 @@ package uk.ac.ic.wlgitbridge.util; import com.google.api.client.repackaged.com.google.common.base.Preconditions; +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; @@ -9,13 +12,8 @@ import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; import org.apache.commons.compress.utils.IOUtils; -import org.apache.commons.io.FileUtils; -import java.io.*; -import java.nio.file.Path; -import java.nio.file.Paths; - -/** +/* * Tar utilities. * * The resource returned by zip and tar are treated as unowned. @@ -25,196 +23,148 @@ import java.nio.file.Paths; * Caller is responsible for all resources. */ public class Tar { - public static class gzip { + public static class gzip { - public static InputStream zip( - File fileOrDir - ) throws IOException { - return zip(fileOrDir, null); - } - - public static InputStream zip( - File fileOrDir, - long[] sizePtr - ) throws IOException { - File tmp = File.createTempFile(fileOrDir.getName(), ".tar.gz"); - tmp.deleteOnExit(); - OutputStream target = new FileOutputStream(tmp); - /* Closes target */ - try (OutputStream gz = new GzipCompressorOutputStream(target)) { - tarTo(fileOrDir, gz); - } catch (IOException e) { - tmp.delete(); - throw e; - } - if (sizePtr != null) { - sizePtr[0] = tmp.length(); - } - return new DeletingFileInputStream(tmp); - } - - public static void unzip( - InputStream targz, - File parentDir - ) throws IOException { - /* GzipCompressorInputStream does not need closing - Closing it would close targz which we should not do */ - InputStream tar = new GzipCompressorInputStream(targz); - untar(tar, parentDir); - } + public static InputStream zip(File fileOrDir) throws IOException { + return zip(fileOrDir, null); } - public static class bz2 { - - public static InputStream zip( - File fileOrDir - ) throws IOException { - return zip(fileOrDir, null); - } - - public static InputStream zip( - File fileOrDir, - long[] sizePtr - ) throws IOException { - File tmp = File.createTempFile(fileOrDir.getName(), ".tar.bz2"); - tmp.deleteOnExit(); - OutputStream target = new FileOutputStream(tmp); - /* Closes target */ - try (OutputStream bzip2 = new BZip2CompressorOutputStream(target)) { - tarTo(fileOrDir, bzip2); - } catch (IOException e) { - tmp.delete(); - throw e; - } - if (sizePtr != null) { - sizePtr[0] = tmp.length(); - } - return new DeletingFileInputStream(tmp); - } - - public static void unzip( - InputStream tarbz2, - File parentDir - ) throws IOException { - /* BZip2CompressorInputStream does not need closing - Closing it would close tarbz2 which we should not do */ - InputStream tar = new BZip2CompressorInputStream(tarbz2); - untar(tar, parentDir); - } - + public static InputStream zip(File fileOrDir, long[] sizePtr) throws IOException { + File tmp = File.createTempFile(fileOrDir.getName(), ".tar.gz"); + tmp.deleteOnExit(); + OutputStream target = new FileOutputStream(tmp); + /* Closes target */ + try (OutputStream gz = new GzipCompressorOutputStream(target)) { + tarTo(fileOrDir, gz); + } catch (IOException e) { + tmp.delete(); + throw e; + } + if (sizePtr != null) { + sizePtr[0] = tmp.length(); + } + return new DeletingFileInputStream(tmp); } - private Tar() {} + public static void unzip(InputStream targz, File parentDir) throws IOException { + /* GzipCompressorInputStream does not need closing + Closing it would close targz which we should not do */ + InputStream tar = new GzipCompressorInputStream(targz); + untar(tar, parentDir); + } + } - public static InputStream tar(File fileOrDir) throws IOException { - File tmp = File.createTempFile(fileOrDir.getName(), ".tar"); - tmp.deleteOnExit(); - try (FileOutputStream target = new FileOutputStream(tmp)) { - tarTo(fileOrDir, target); - return new DeletingFileInputStream(tmp); - } catch (IOException e) { - tmp.delete(); - throw e; - } + public static class bz2 { + + public static InputStream zip(File fileOrDir) throws IOException { + return zip(fileOrDir, null); } - public static void tarTo( - File fileOrDir, - OutputStream target - ) throws IOException { - try (TarArchiveOutputStream tout = new TarArchiveOutputStream(target)) { - addTarEntry( - tout, - Paths.get(fileOrDir.getParentFile().getAbsolutePath()), - fileOrDir - ); - } + public static InputStream zip(File fileOrDir, long[] sizePtr) throws IOException { + File tmp = File.createTempFile(fileOrDir.getName(), ".tar.bz2"); + tmp.deleteOnExit(); + OutputStream target = new FileOutputStream(tmp); + /* Closes target */ + try (OutputStream bzip2 = new BZip2CompressorOutputStream(target)) { + tarTo(fileOrDir, bzip2); + } catch (IOException e) { + tmp.delete(); + throw e; + } + if (sizePtr != null) { + sizePtr[0] = tmp.length(); + } + return new DeletingFileInputStream(tmp); } - public static void untar( - InputStream tar, - File parentDir - ) throws IOException { - TarArchiveInputStream tin = new TarArchiveInputStream(tar); - ArchiveEntry e; - while ((e = tin.getNextEntry()) != null) { - File f = new File(parentDir, e.getName()); - f.setLastModified(e.getLastModifiedDate().getTime()); - f.getParentFile().mkdirs(); - if (e.isDirectory()) { - f.mkdir(); - continue; - } - long size = e.getSize(); - checkFileSize(size); - try (OutputStream out = new FileOutputStream(f)) { - /* TarInputStream pretends each - entry's EOF is the stream's EOF */ - IOUtils.copy(tin, out); - } - } + public static void unzip(InputStream tarbz2, File parentDir) throws IOException { + /* BZip2CompressorInputStream does not need closing + Closing it would close tarbz2 which we should not do */ + InputStream tar = new BZip2CompressorInputStream(tarbz2); + untar(tar, parentDir); } + } - private static void checkFileSize(long size) { - Preconditions.checkArgument( - size >= 0 && size <= Integer.MAX_VALUE, - "file too big (" + size + " B): " + - "tarTo should have thrown an IOException" - ); + private Tar() {} + + public static InputStream tar(File fileOrDir) throws IOException { + File tmp = File.createTempFile(fileOrDir.getName(), ".tar"); + tmp.deleteOnExit(); + try (FileOutputStream target = new FileOutputStream(tmp)) { + tarTo(fileOrDir, target); + return new DeletingFileInputStream(tmp); + } catch (IOException e) { + tmp.delete(); + throw e; } + } - private static void addTarEntry( - TarArchiveOutputStream tout, - Path base, - File fileOrDir - ) throws IOException { - if (fileOrDir.isDirectory()) { - addTarDir(tout, base, fileOrDir); - } else if (fileOrDir.isFile()) { - addTarFile(tout, base, fileOrDir); - } else { - throw new IllegalArgumentException( - "invalid file or dir: " + fileOrDir - ); - } + public static void tarTo(File fileOrDir, OutputStream target) throws IOException { + try (TarArchiveOutputStream tout = new TarArchiveOutputStream(target)) { + addTarEntry(tout, Paths.get(fileOrDir.getParentFile().getAbsolutePath()), fileOrDir); } + } - private static void addTarDir( - TarArchiveOutputStream tout, - Path base, - File dir - ) throws IOException { - Preconditions.checkArgument(dir.isDirectory()); - String name = base.relativize( - Paths.get(dir.getAbsolutePath()) - ).toString(); - ArchiveEntry entry = tout.createArchiveEntry(dir, name); - tout.putArchiveEntry(entry); - tout.closeArchiveEntry(); - for (File f : dir.listFiles()) { - addTarEntry(tout, base, f); - } + public static void untar(InputStream tar, File parentDir) throws IOException { + TarArchiveInputStream tin = new TarArchiveInputStream(tar); + ArchiveEntry e; + while ((e = tin.getNextEntry()) != null) { + File f = new File(parentDir, e.getName()); + f.setLastModified(e.getLastModifiedDate().getTime()); + f.getParentFile().mkdirs(); + if (e.isDirectory()) { + f.mkdir(); + continue; + } + long size = e.getSize(); + checkFileSize(size); + try (OutputStream out = new FileOutputStream(f)) { + /* TarInputStream pretends each + entry's EOF is the stream's EOF */ + IOUtils.copy(tin, out); + } } + } - private static void addTarFile( - TarArchiveOutputStream tout, - Path base, - File file - ) throws IOException { - Preconditions.checkArgument( - file.isFile(), - "given file" + - " is not file: %s", file); - checkFileSize(file.length()); - String name = base.relativize( - Paths.get(file.getAbsolutePath()) - ).toString(); - ArchiveEntry entry = tout.createArchiveEntry(file, name); - tout.putArchiveEntry(entry); - try (InputStream in = new FileInputStream(file)) { - IOUtils.copy(in, tout); - } - tout.closeArchiveEntry(); + private static void checkFileSize(long size) { + Preconditions.checkArgument( + size >= 0 && size <= Integer.MAX_VALUE, + "file too big (" + size + " B): " + "tarTo should have thrown an IOException"); + } + + private static void addTarEntry(TarArchiveOutputStream tout, Path base, File fileOrDir) + throws IOException { + if (fileOrDir.isDirectory()) { + addTarDir(tout, base, fileOrDir); + } else if (fileOrDir.isFile()) { + addTarFile(tout, base, fileOrDir); + } else { + throw new IllegalArgumentException("invalid file or dir: " + fileOrDir); } + } + private static void addTarDir(TarArchiveOutputStream tout, Path base, File dir) + throws IOException { + Preconditions.checkArgument(dir.isDirectory()); + String name = base.relativize(Paths.get(dir.getAbsolutePath())).toString(); + ArchiveEntry entry = tout.createArchiveEntry(dir, name); + tout.putArchiveEntry(entry); + tout.closeArchiveEntry(); + for (File f : dir.listFiles()) { + addTarEntry(tout, base, f); + } + } + + private static void addTarFile(TarArchiveOutputStream tout, Path base, File file) + throws IOException { + Preconditions.checkArgument(file.isFile(), "given file" + " is not file: %s", file); + checkFileSize(file.length()); + String name = base.relativize(Paths.get(file.getAbsolutePath())).toString(); + ArchiveEntry entry = tout.createArchiveEntry(file, name); + tout.putArchiveEntry(entry); + try (InputStream in = new FileInputStream(file)) { + IOUtils.copy(in, tout); + } + tout.closeArchiveEntry(); + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/TimerUtils.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/TimerUtils.java index 5b2893870e..f432f1d8c9 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/TimerUtils.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/TimerUtils.java @@ -2,22 +2,21 @@ package uk.ac.ic.wlgitbridge.util; import java.util.TimerTask; -/** +/* * Created by winston on 23/08/2016. */ public class TimerUtils { - public static TimerTask makeTimerTask(Runnable lamb) { - return new TimerTask() { - @Override - public void run() { - try { - lamb.run(); - } catch (Throwable t) { - Log.warn("Error on timer", t); - } - } - }; - } - + public static TimerTask makeTimerTask(Runnable lamb) { + return new TimerTask() { + @Override + public void run() { + try { + lamb.run(); + } catch (Throwable t) { + Log.warn("Error on timer", t); + } + } + }; + } } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Util.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Util.java index 50b09f68bb..57eeb11899 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Util.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Util.java @@ -2,204 +2,186 @@ package uk.ac.ic.wlgitbridge.util; import com.google.gson.JsonElement; import com.google.gson.JsonObject; - import java.io.*; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; -/** +/* * Created by Winston on 19/11/14. */ public class Util { - private static String SERVICE_NAME; - private static String HOSTNAME; - private static int PORT; - private static String POSTBACK_URL; - private static final DateFormat dateFormat - = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"); + private static String SERVICE_NAME; + private static String HOSTNAME; + private static int PORT; + private static String POSTBACK_URL; + private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"); - public static String entries(int entries) { - if (entries == 1) { - return "entry"; - } else { - return "entries"; - } + public static String entries(int entries) { + if (entries == 1) { + return "entry"; + } else { + return "entries"; } + } - public static int booleanToInt(boolean b) { - if (b) { - return 1; - } else { - return 0; - } + public static int booleanToInt(boolean b) { + if (b) { + return 1; + } else { + return 0; } + } - public static boolean intToBoolean(int i) { - return i != 0; + public static boolean intToBoolean(int i) { + return i != 0; + } + + private static String removeAllSuffix(String str, String suffix) { + int lastIndexOfSuffix; + String result = str; + while ((lastIndexOfSuffix = result.lastIndexOf(suffix)) > -1) { + result = result.substring(0, lastIndexOfSuffix); } + return result; + } - private static String removeAllSuffix(String str, String suffix) { - int lastIndexOfSuffix; - String result = str; - while ((lastIndexOfSuffix = result.lastIndexOf(suffix)) > -1) { - result = result.substring(0, lastIndexOfSuffix); - } - return result; + /* removeAllSuffixes("something.git///", "/", ".git") => "something" */ + public static String removeAllSuffixes(String str, String... suffixes) { + String result = str; + for (String suffix : suffixes) { + result = removeAllSuffix(result, suffix); } + return result; + } - /* removeAllSuffixes("something.git///", "/", ".git") => "something" */ - public static String removeAllSuffixes(String str, String... suffixes) { - String result = str; - for (String suffix : suffixes) { - result = removeAllSuffix(result, suffix); - } - return result; + public static String getContentsOfReader(BufferedReader reader) throws IOException { + StringBuilder sb = new StringBuilder(); + for (String line; (line = reader.readLine()) != null; ) { + sb.append(line); } + return sb.toString(); + } - public static String getContentsOfReader( - BufferedReader reader - ) throws IOException { - StringBuilder sb = new StringBuilder(); - for (String line; (line = reader.readLine()) != null;) { - sb.append(line); - } - return sb.toString(); + public static void setServiceName(String serviceName) { + SERVICE_NAME = serviceName; + } + + public static String getServiceName() { + return SERVICE_NAME; + } + + public static int getPort() { + return PORT; + } + + public static void setPort(int port) { + PORT = port; + } + + public static void setPostbackURL(String postbackURL) { + POSTBACK_URL = postbackURL; + } + + public static String getPostbackURL() { + return POSTBACK_URL; + } + + public static void deleteDirectory(File directory) { + if (directory != null) { + deleteInDirectory(directory); + directory.delete(); } + } - public static void setServiceName(String serviceName) { - SERVICE_NAME = serviceName; + public static void deleteInDirectory(File directory) { + if (directory != null) { + deleteInDirectoryApartFrom(directory); } + } - public static String getServiceName() { - return SERVICE_NAME; - } - - public static int getPort() { - return PORT; - } - - public static void setPort(int port) { - PORT = port; - } - - public static void setPostbackURL(String postbackURL) { - POSTBACK_URL = postbackURL; - } - - public static String getPostbackURL() { - return POSTBACK_URL; - } - - public static void deleteDirectory(File directory) { - if (directory != null) { - deleteInDirectory(directory); - directory.delete(); - } - } - - public static void deleteInDirectory(File directory) { - if (directory != null) { - deleteInDirectoryApartFrom(directory); - } - } - - public static void deleteInDirectoryApartFrom( - File directory, - String... apartFrom - ) { - if (directory != null) { - Set excluded = new HashSet<>(Arrays.asList(apartFrom)); - File [] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (!excluded.contains(file.getName())) { - if (file.isDirectory()) { - deleteInDirectory(file); - } - file.delete(); - Log.debug("Deleted file: {}", file.getAbsolutePath()); - } - } + public static void deleteInDirectoryApartFrom(File directory, String... apartFrom) { + if (directory != null) { + Set excluded = new HashSet<>(Arrays.asList(apartFrom)); + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (!excluded.contains(file.getName())) { + if (file.isDirectory()) { + deleteInDirectory(file); } + file.delete(); + Log.debug("Deleted file: {}", file.getAbsolutePath()); + } } + } } + } - public static List linesFromStream( - InputStream stream, - int skip, - String trimSuffix - ) throws IOException { - List lines = new ArrayList<>(); - BufferedReader reader = new BufferedReader( - new InputStreamReader(stream) - ); - String line; - for (int i = 0; i < skip; i++) { - reader.readLine(); - } - while ((line = reader.readLine()) != null) { - String trim = line.trim(); - trim = trim.replaceAll("\\p{C}", ""); - int endIndex = trim.lastIndexOf(trimSuffix); - if (endIndex >= 0) { - trim = trim.substring(0, endIndex); - } - lines.add(trim); - } - return lines; + public static List linesFromStream(InputStream stream, int skip, String trimSuffix) + throws IOException { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + String line; + for (int i = 0; i < skip; i++) { + reader.readLine(); } - - public static String fromStream( - InputStream stream, - int skip - ) throws IOException { - BufferedReader reader = new BufferedReader( - new InputStreamReader(stream) - ); - StringBuilder out = new StringBuilder(); - String newLine = System.getProperty("line.separator"); - String line; - for (int i = 0; i < skip; i++) { - reader.readLine(); - } - while ((line = reader.readLine()) != null) { - out.append(line); - out.append(newLine); - } - return out.toString(); + while ((line = reader.readLine()) != null) { + String trim = line.trim(); + trim = trim.replaceAll("\\p{C}", ""); + int endIndex = trim.lastIndexOf(trimSuffix); + if (endIndex >= 0) { + trim = trim.substring(0, endIndex); + } + lines.add(trim); } + return lines; + } - public static String fromStream(InputStream stream) throws IOException { - return fromStream(stream, 0); + public static String fromStream(InputStream stream, int skip) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + StringBuilder out = new StringBuilder(); + String newLine = System.getProperty("line.separator"); + String line; + for (int i = 0; i < skip; i++) { + reader.readLine(); } + while ((line = reader.readLine()) != null) { + out.append(line); + out.append(newLine); + } + return out.toString(); + } - public static String getCodeFromResponse(JsonObject json) { - String code = "error"; - JsonElement codeElement = json.get("code"); + public static String fromStream(InputStream stream) throws IOException { + return fromStream(stream, 0); + } - if (codeElement == null) { - String error = "Unexpected error"; - Log.warn("Unexpected response from API:"); - Log.warn(json.toString()); - Log.warn("End of response"); - JsonElement statusElement = json.get("status"); - if (statusElement != null) { - String status = statusElement.getAsString(); - if (status.equals("422")) { - error = "Unprocessable entity"; - } else if (status.equals("404")) { - error = "Not found"; - } else if (status.equals("403")) { - error = "Forbidden"; - } - } - throw new RuntimeException(error); - } else { - code = codeElement.getAsString(); + public static String getCodeFromResponse(JsonObject json) { + String code = "error"; + JsonElement codeElement = json.get("code"); + + if (codeElement == null) { + String error = "Unexpected error"; + Log.warn("Unexpected response from API:"); + Log.warn(json.toString()); + Log.warn("End of response"); + JsonElement statusElement = json.get("status"); + if (statusElement != null) { + String status = statusElement.getAsString(); + if (status.equals("422")) { + error = "Unprocessable entity"; + } else if (status.equals("404")) { + error = "Not found"; + } else if (status.equals("403")) { + error = "Forbidden"; } - return code; + } + throw new RuntimeException(error); + } else { + code = codeElement.getAsString(); } - + return code; + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java index 580572c99e..e250798652 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/WLGitBridgeIntegrationTest.java @@ -1,36 +1,7 @@ package uk.ac.ic.wlgitbridge.application; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import static org.asynchttpclient.Dsl.*; - -import org.apache.http.HttpResponse; -import org.apache.http.ParseException; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.HttpEntity; -import org.apache.http.util.EntityUtils; -import org.apache.http.ParseException; - -import org.asynchttpclient.*; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import uk.ac.ic.wlgitbridge.bridge.swap.job.SwapJobConfig; -import uk.ac.ic.wlgitbridge.snapshot.servermock.server.MockSnapshotServer; -import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState; -import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIStateBuilder; -import uk.ac.ic.wlgitbridge.snapshot.servermock.util.FileUtil; -import uk.ac.ic.wlgitbridge.util.Util; +import static org.junit.Assert.*; import java.io.*; import java.nio.charset.StandardCharsets; @@ -41,1097 +12,1462 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.ParseException; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.asynchttpclient.*; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import uk.ac.ic.wlgitbridge.bridge.swap.job.SwapJobConfig; +import uk.ac.ic.wlgitbridge.snapshot.servermock.server.MockSnapshotServer; +import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState; +import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIStateBuilder; +import uk.ac.ic.wlgitbridge.snapshot.servermock.util.FileUtil; +import uk.ac.ic.wlgitbridge.util.Util; -import static org.junit.Assert.*; - -/** +/* * Created by Winston on 11/01/15. */ public class WLGitBridgeIntegrationTest { - private Runtime runtime = Runtime.getRuntime(); + private Runtime runtime = Runtime.getRuntime(); - private static final String PROJECT_ID = "000000000000000000000000"; - private static final String PROJECT_ID1 = "111111111111111111111111"; - private static final String PROJECT_ID2 = "222222222222222222222222"; + private static final String PROJECT_ID = "000000000000000000000000"; + private static final String PROJECT_ID1 = "111111111111111111111111"; + private static final String PROJECT_ID2 = "222222222222222222222222"; - private Map> states = new HashMap>() {{ - put("canCloneARepository", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneARepository/state/state.json")).build()); - }}); - put("canCloneMultipleRepositories", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneMultipleRepositories/state/state.json")).build()); - }}); - put("cannotCloneAProtectedProjectWithoutAuthentication", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneAProtectedProjectWithoutAuthentication/state/state.json")).build()); - }}); - put("cannotCloneA4xxProject", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneA4xxProject/state/state.json")).build()); - }}); - put("cannotCloneAMissingProject", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneAMissingProject/state/state.json")).build()); - }}); - put("canPullAModifiedTexFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedTexFile/base/state.json")).build()); - put("withModifiedTexFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedTexFile/withModifiedTexFile/state.json")).build()); - }}); - put("canPullADeletedTexFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedTexFile/base/state.json")).build()); - put("withDeletedTexFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedTexFile/withDeletedTexFile/state.json")).build()); - }}); - put("canPullAModifiedBinaryFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedBinaryFile/base/state.json")).build()); - put("withModifiedBinaryFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedBinaryFile/withModifiedBinaryFile/state.json")).build()); - }}); - put("canPullADeletedBinaryFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedBinaryFile/base/state.json")).build()); - put("withDeletedBinaryFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedBinaryFile/withDeletedBinaryFile/state.json")).build()); - }}); - put("canPullADuplicateBinaryFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADuplicateBinaryFile/base/state.json")).build()); - put("withDuplicateBinaryFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/state.json")).build()); - }}); - put("canCloneDuplicateBinaryFiles", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneDuplicateBinaryFiles/state/state.json")).build()); - }}); - put("canPullUpdatedBinaryFiles", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullUpdatedBinaryFiles/base/state.json")).build()); - put("withUpdatedBinaryFiles", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/state.json")).build()); - }}); - put("canPullAModifiedNestedFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedNestedFile/base/state.json")).build()); - put("withModifiedNestedFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedNestedFile/withModifiedNestedFile/state.json")).build()); - }}); - put("canPullDeletedNestedFiles", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullDeletedNestedFiles/base/state.json")).build()); - put("withDeletedNestedFiles", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullDeletedNestedFiles/withDeletedNestedFiles/state.json")).build()); - }}); - put("canPushFilesSuccessfully", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canPushFilesSuccessfully/state/state.json")).build()); - }}); - put("pushFailsOnFirstStageOutOfDate", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnFirstStageOutOfDate/state/state.json")).build()); - }}); - put("pushFailsOnSecondStageOutOfDate", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnSecondStageOutOfDate/state/state.json")).build()); - }}); - put("pushFailsOnInvalidFiles", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnInvalidFiles/state/state.json")).build()); - }}); - put("pushFailsOnInvalidProject", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnInvalidProject/state/state.json")).build()); - }}); - put("pushFailsOnUnexpectedError", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnUnexpectedError/state/state.json")).build()); - }}); - put("pushSucceedsAfterRemovingInvalidFiles", new HashMap() {{ - put("invalidState", new SnapshotAPIStateBuilder(getResourceAsStream("/pushSucceedsAfterRemovingInvalidFiles/invalidState/state.json")).build()); - put("validState", new SnapshotAPIStateBuilder(getResourceAsStream("/pushSucceedsAfterRemovingInvalidFiles/validState/state.json")).build()); - }}); - put("canServePushedFiles", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canServePushedFiles/state/state.json")).build()); - }}); - put("wlgbCanSwapProjects", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/wlgbCanSwapProjects/state/state.json")).build()); - }}); - put("pushSubmoduleFailsWithInvalidGitRepo", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushSubmoduleFailsWithInvalidGitRepo/state/state.json")).build()); - }}); - put("canMigrateRepository", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canMigrateRepository/state/state.json")).build()); - }}); - put("skipMigrationWhenMigratedFromMissing", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/skipMigrationWhenMigratedFromMissing/state/state.json")).build()); - }}); - put("canCloneAMigratedRepositoryWithoutChanges", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneAMigratedRepositoryWithoutChanges/state/state.json")).build()); - }}); - put("rejectV1Repository", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/rejectV1Repository/state/state.json")).build()); - }}); - put("cannotCloneAHasDotGitProject", new HashMap() {{ - put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneAHasDotGitProject/state/state.json")).build()); - }}); - put("canPullIgnoredForceAddedFile", new HashMap() {{ - put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullIgnoredForceAddedFile/base/state.json")).build()); - put("withUpdatedMainFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullIgnoredForceAddedFile/withUpdatedMainFile/state.json")).build()); - }}); - }}; - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - private MockSnapshotServer server; - private GitBridgeApp wlgb; - private File dir; - - @Before - public void setUp() throws Exception { - dir = folder.newFolder(); - } - - @After - public void tearDown() { - server.stop(); - wlgb.stop(); - } - - private void gitConfig(File dir) throws IOException, InterruptedException { - assertEquals(0, runtime.exec( - "git config user.name TEST", null, dir - ).waitFor()); - assertEquals(0, runtime.exec( - "git config user.email test@test.com", null, dir - ).waitFor()); - assertEquals(0, runtime.exec( - "git config push.default matching", null, dir - ).waitFor()); - } - - private File gitClone(String repositoryName, int port, File dir) throws IOException, InterruptedException { - String repo = "git clone http://git:password@127.0.0.1:" + port + "/" + repositoryName; - Process gitProcess = runtime.exec(repo, null, dir); - int exitCode = gitProcess.waitFor(); - if (exitCode != 0) { - System.err.println("git clone failed. Dumping stderr and stdout."); - System.err.println( - IOUtils.toString( - gitProcess.getErrorStream(), - StandardCharsets.UTF_8 - ) - ); - System.err.println( - IOUtils.toString( - gitProcess.getInputStream(), - StandardCharsets.UTF_8 - ) - ); - fail("git clone failed"); + private Map> states = + new HashMap>() { + { + put( + "canCloneARepository", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canCloneARepository/state/state.json")) + .build()); + } + }); + put( + "canCloneMultipleRepositories", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canCloneMultipleRepositories/state/state.json")) + .build()); + } + }); + put( + "cannotCloneAProtectedProjectWithoutAuthentication", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/cannotCloneAProtectedProjectWithoutAuthentication/state/state.json")) + .build()); + } + }); + put( + "cannotCloneA4xxProject", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/cannotCloneA4xxProject/state/state.json")) + .build()); + } + }); + put( + "cannotCloneAMissingProject", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/cannotCloneAMissingProject/state/state.json")) + .build()); + } + }); + put( + "canPullAModifiedTexFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullAModifiedTexFile/base/state.json")) + .build()); + put( + "withModifiedTexFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullAModifiedTexFile/withModifiedTexFile/state.json")) + .build()); + } + }); + put( + "canPullADeletedTexFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullADeletedTexFile/base/state.json")) + .build()); + put( + "withDeletedTexFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullADeletedTexFile/withDeletedTexFile/state.json")) + .build()); + } + }); + put( + "canPullAModifiedBinaryFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullAModifiedBinaryFile/base/state.json")) + .build()); + put( + "withModifiedBinaryFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullAModifiedBinaryFile/withModifiedBinaryFile/state.json")) + .build()); + } + }); + put( + "canPullADeletedBinaryFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullADeletedBinaryFile/base/state.json")) + .build()); + put( + "withDeletedBinaryFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullADeletedBinaryFile/withDeletedBinaryFile/state.json")) + .build()); + } + }); + put( + "canPullADuplicateBinaryFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullADuplicateBinaryFile/base/state.json")) + .build()); + put( + "withDuplicateBinaryFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullADuplicateBinaryFile/withDuplicateBinaryFile/state.json")) + .build()); + } + }); + put( + "canCloneDuplicateBinaryFiles", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canCloneDuplicateBinaryFiles/state/state.json")) + .build()); + } + }); + put( + "canPullUpdatedBinaryFiles", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullUpdatedBinaryFiles/base/state.json")) + .build()); + put( + "withUpdatedBinaryFiles", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/state.json")) + .build()); + } + }); + put( + "canPullAModifiedNestedFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullAModifiedNestedFile/base/state.json")) + .build()); + put( + "withModifiedNestedFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullAModifiedNestedFile/withModifiedNestedFile/state.json")) + .build()); + } + }); + put( + "canPullDeletedNestedFiles", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullDeletedNestedFiles/base/state.json")) + .build()); + put( + "withDeletedNestedFiles", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullDeletedNestedFiles/withDeletedNestedFiles/state.json")) + .build()); + } + }); + put( + "canPushFilesSuccessfully", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPushFilesSuccessfully/state/state.json")) + .build()); + } + }); + put( + "pushFailsOnFirstStageOutOfDate", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/pushFailsOnFirstStageOutOfDate/state/state.json")) + .build()); + } + }); + put( + "pushFailsOnSecondStageOutOfDate", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/pushFailsOnSecondStageOutOfDate/state/state.json")) + .build()); + } + }); + put( + "pushFailsOnInvalidFiles", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/pushFailsOnInvalidFiles/state/state.json")) + .build()); + } + }); + put( + "pushFailsOnInvalidProject", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/pushFailsOnInvalidProject/state/state.json")) + .build()); + } + }); + put( + "pushFailsOnUnexpectedError", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/pushFailsOnUnexpectedError/state/state.json")) + .build()); + } + }); + put( + "pushSucceedsAfterRemovingInvalidFiles", + new HashMap() { + { + put( + "invalidState", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/pushSucceedsAfterRemovingInvalidFiles/invalidState/state.json")) + .build()); + put( + "validState", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/pushSucceedsAfterRemovingInvalidFiles/validState/state.json")) + .build()); + } + }); + put( + "canServePushedFiles", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canServePushedFiles/state/state.json")) + .build()); + } + }); + put( + "wlgbCanSwapProjects", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/wlgbCanSwapProjects/state/state.json")) + .build()); + } + }); + put( + "pushSubmoduleFailsWithInvalidGitRepo", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/pushSubmoduleFailsWithInvalidGitRepo/state/state.json")) + .build()); + } + }); + put( + "canMigrateRepository", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canMigrateRepository/state/state.json")) + .build()); + } + }); + put( + "skipMigrationWhenMigratedFromMissing", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/skipMigrationWhenMigratedFromMissing/state/state.json")) + .build()); + } + }); + put( + "canCloneAMigratedRepositoryWithoutChanges", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canCloneAMigratedRepositoryWithoutChanges/state/state.json")) + .build()); + } + }); + put( + "rejectV1Repository", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/rejectV1Repository/state/state.json")) + .build()); + } + }); + put( + "cannotCloneAHasDotGitProject", + new HashMap() { + { + put( + "state", + new SnapshotAPIStateBuilder( + getResourceAsStream("/cannotCloneAHasDotGitProject/state/state.json")) + .build()); + } + }); + put( + "canPullIgnoredForceAddedFile", + new HashMap() { + { + put( + "base", + new SnapshotAPIStateBuilder( + getResourceAsStream("/canPullIgnoredForceAddedFile/base/state.json")) + .build()); + put( + "withUpdatedMainFile", + new SnapshotAPIStateBuilder( + getResourceAsStream( + "/canPullIgnoredForceAddedFile/withUpdatedMainFile/state.json")) + .build()); + } + }); } - File repositoryDir = new File(dir, repositoryName); - gitConfig(repositoryDir); - return repositoryDir; + }; + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + private MockSnapshotServer server; + private GitBridgeApp wlgb; + private File dir; + + @Before + public void setUp() throws Exception { + dir = folder.newFolder(); + } + + @After + public void tearDown() { + server.stop(); + wlgb.stop(); + } + + private void gitConfig(File dir) throws IOException, InterruptedException { + assertEquals(0, runtime.exec("git config user.name TEST", null, dir).waitFor()); + assertEquals(0, runtime.exec("git config user.email test@test.com", null, dir).waitFor()); + assertEquals(0, runtime.exec("git config push.default matching", null, dir).waitFor()); + } + + private File gitClone(String repositoryName, int port, File dir) + throws IOException, InterruptedException { + String repo = "git clone http://git:password@127.0.0.1:" + port + "/" + repositoryName; + Process gitProcess = runtime.exec(repo, null, dir); + int exitCode = gitProcess.waitFor(); + if (exitCode != 0) { + System.err.println("git clone failed. Dumping stderr and stdout."); + System.err.println(IOUtils.toString(gitProcess.getErrorStream(), StandardCharsets.UTF_8)); + System.err.println(IOUtils.toString(gitProcess.getInputStream(), StandardCharsets.UTF_8)); + fail("git clone failed"); } - - private void gitInit(File dir) throws IOException, InterruptedException { - assertEquals(0, runtime.exec( - "git init", null, dir - ).waitFor()); - gitConfig(dir); - } - - private void gitAdd(File dir) throws IOException, InterruptedException { - assertEquals(0, runtime.exec( - "git add -A", null, dir - ).waitFor()); - } - - private void gitCommit( - File dir, String msg - ) throws IOException, InterruptedException { - assertEquals(0, runtime.exec( - "git commit -m \"" + msg + "\"", null, dir - ).waitFor()); - } - - private Process gitPush(File dir) throws IOException, InterruptedException { - return gitPush(dir, 0); - } - - private Process gitPush( - File dir, int exit - ) throws IOException, InterruptedException { - Process ret = runtime.exec( - "git push", null, dir - ); - assertEquals(exit, ret.waitFor()); - return ret; - } - - private void gitPull(File dir) throws IOException, InterruptedException { - assertEquals(0, runtime.exec( - "git pull", null, dir - ).waitFor()); - } - - @Test - public void canCloneARepository() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3857, getResource("/canCloneARepository").toFile()); - server.start(); - server.setState(states.get("canCloneARepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33857, 3857) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33857, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneARepository/state/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canCloneMultipleRepositories() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3858, getResource("/canCloneMultipleRepositories").toFile()); - server.start(); - server.setState(states.get("canCloneMultipleRepositories").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33858, 3858) - }); - wlgb.run(); - File testproj1Dir = gitClone(PROJECT_ID1, 33858, dir); - File testproj2Dir = gitClone(PROJECT_ID2, 33858, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/" + PROJECT_ID1), testproj1Dir.toPath())); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/" + PROJECT_ID2), testproj2Dir.toPath())); - } - - @Test - public void canPullAModifiedTexFile() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3859, getResource("/canPullAModifiedTexFile").toFile()); - server.start(); - server.setState(states.get("canPullAModifiedTexFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33859, 3859) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33859, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullAModifiedTexFile").get("withModifiedTexFile")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/withModifiedTexFile/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullADeletedTexFile() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3860, getResource("/canPullADeletedTexFile").toFile()); - server.start(); - server.setState(states.get("canPullADeletedTexFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33860, 3860) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33860, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullADeletedTexFile").get("withDeletedTexFile")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/withDeletedTexFile/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullAModifiedBinaryFile() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3862, getResource("/canPullAModifiedBinaryFile").toFile()); - server.start(); - server.setState(states.get("canPullAModifiedBinaryFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33862, 3862) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33862, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullAModifiedBinaryFile").get("withModifiedBinaryFile")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/withModifiedBinaryFile/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullADeletedBinaryFile() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3863, getResource("/canPullADeletedBinaryFile").toFile()); - server.start(); - server.setState(states.get("canPullADeletedBinaryFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33863, 3863) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33863, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullADeletedBinaryFile").get("withDeletedBinaryFile")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/withDeletedBinaryFile/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullADuplicateBinaryFile() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(4001, getResource("/canPullADuplicateBinaryFile").toFile()); - server.start(); - server.setState(states.get("canPullADuplicateBinaryFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(44001, 4001) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 44001, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullADuplicateBinaryFile").get("withDuplicateBinaryFile")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canCloneDuplicateBinaryFiles() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(4002, getResource("/canCloneDuplicateBinaryFiles").toFile()); - server.start(); - server.setState(states.get("canCloneDuplicateBinaryFiles").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(44002, 4002) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 44002, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneDuplicateBinaryFiles/state/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullUpdatedBinaryFiles() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(4003, getResource("/canPullUpdatedBinaryFiles").toFile()); - server.start(); - server.setState(states.get("canPullUpdatedBinaryFiles").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(44003, 4003) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 44003, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullUpdatedBinaryFiles").get("withUpdatedBinaryFiles")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullAModifiedNestedFile() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3864, getResource("/canPullAModifiedNestedFile").toFile()); - server.start(); - server.setState(states.get("canPullAModifiedNestedFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33864, 3864) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33864, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullAModifiedNestedFile").get("withModifiedNestedFile")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/withModifiedNestedFile/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPullDeletedNestedFiles() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3865, getResource("/canPullDeletedNestedFiles").toFile()); - server.start(); - server.setState(states.get("canPullDeletedNestedFiles").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33865, 3865) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33865, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/base/" + PROJECT_ID), testprojDir.toPath())); - server.setState(states.get("canPullDeletedNestedFiles").get("withDeletedNestedFiles")); - gitPull(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/withDeletedNestedFiles/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canPushFilesSuccessfully() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3866, getResource("/canPushFilesSuccessfully").toFile()); - server.start(); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33866, 3866) - }); - wlgb.run(); - server.setState(states.get("canPushFilesSuccessfully").get("state")); - File testprojDir = gitClone(PROJECT_ID, 33866, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPushFilesSuccessfully/state/" + PROJECT_ID), testprojDir.toPath())); - assertEquals(0, runtime.exec("touch push.tex", null, testprojDir).waitFor()); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - gitPush(testprojDir); - } - - private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST = - "error: failed to push some refs to 'http://127.0.0.1:33867/" + PROJECT_ID + "'\n" + - "hint: Updates were rejected because the tip of your current branch is behind\n" + - "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + - "hint: 'git pull ...') before pushing again.\n" + - "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n"; - - @Test - public void pushFailsOnFirstStageOutOfDate() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3867, getResource("/pushFailsOnFirstStageOutOfDate").toFile()); - server.start(); - server.setState(states.get("pushFailsOnFirstStageOutOfDate").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33867, 3867) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33867, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnFirstStageOutOfDate/state/" + PROJECT_ID), testprojDir.toPath())); - runtime.exec("touch push.tex", null, testprojDir).waitFor(); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST, Util.fromStream(push.getErrorStream(), 2)); - } - - private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND = - "error: failed to push some refs to 'http://127.0.0.1:33868/" + PROJECT_ID + "'\n" + - "hint: Updates were rejected because the tip of your current branch is behind\n" + - "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + - "hint: 'git pull ...') before pushing again.\n" + - "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n"; - - @Test - public void pushFailsOnSecondStageOutOfDate() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3868, getResource("/pushFailsOnSecondStageOutOfDate").toFile()); - server.start(); - server.setState(states.get("pushFailsOnSecondStageOutOfDate").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33868, 3868) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33868, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnSecondStageOutOfDate/state/" + PROJECT_ID), testprojDir.toPath())); - runtime.exec("touch push.tex", null, testprojDir).waitFor(); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND, Util.fromStream(push.getErrorStream(), 2)); - } - - private static final List EXPECTED_OUT_PUSH_INVALID_FILES = Arrays.asList( - "remote: hint: You have 4 invalid files in your Overleaf project:", - "remote: hint: file1.invalid (error)", - "remote: hint: file2.exe (invalid file extension)", - "remote: hint: hello world.png (rename to: hello_world.png)", - "remote: hint: an image.jpg (rename to: an_image.jpg)", - "To http://127.0.0.1:33869/" + PROJECT_ID, - "! [remote rejected] master -> master (invalid files)", - "error: failed to push some refs to 'http://127.0.0.1:33869/" + PROJECT_ID + "'" - ); - - @Test - public void pushFailsOnInvalidFiles() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3869, getResource("/pushFailsOnInvalidFiles").toFile()); - server.start(); - server.setState(states.get("pushFailsOnInvalidFiles").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33869, 3869) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33869, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidFiles/state/" + PROJECT_ID), testprojDir.toPath())); - runtime.exec("touch push.tex", null, testprojDir).waitFor(); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); - assertEquals(EXPECTED_OUT_PUSH_INVALID_FILES, actual); - } - - private static final List EXPECTED_OUT_PUSH_INVALID_PROJECT = Arrays.asList( - "remote: hint: project: no main file", - "remote: hint: The project would have no (editable) main .tex file.", - "To http://127.0.0.1:33870/" + PROJECT_ID, - "! [remote rejected] master -> master (invalid project)", - "error: failed to push some refs to 'http://127.0.0.1:33870/" + PROJECT_ID + "'" - ); - - @Test - public void pushFailsOnInvalidProject() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3870, getResource("/pushFailsOnInvalidProject").toFile()); - server.start(); - server.setState(states.get("pushFailsOnInvalidProject").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33870, 3870) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33870, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidProject/state/" + PROJECT_ID), testprojDir.toPath())); - runtime.exec("touch push.tex", null, testprojDir).waitFor(); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); - assertEquals(EXPECTED_OUT_PUSH_INVALID_PROJECT, actual); - } - - private static final List EXPECTED_OUT_PUSH_UNEXPECTED_ERROR = Arrays.asList( - "remote: hint: There was an internal error with the Overleaf server.", - "remote: hint: Please contact Overleaf.", - "To http://127.0.0.1:33871/" + PROJECT_ID, - "! [remote rejected] master -> master (Overleaf error)", - "error: failed to push some refs to 'http://127.0.0.1:33871/" + PROJECT_ID + "'" - ); - - /* this one prints a stack trace */ - @Test - public void pushFailsOnUnexpectedError() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3871, getResource("/pushFailsOnUnexpectedError").toFile()); - server.start(); - server.setState(states.get("pushFailsOnUnexpectedError").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33871, 3871) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33871, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnUnexpectedError/state/" + PROJECT_ID), testprojDir.toPath())); - runtime.exec("touch push.tex", null, testprojDir).waitFor(); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); - assertEquals(EXPECTED_OUT_PUSH_UNEXPECTED_ERROR, actual); - } - - private static final List EXPECTED_OUT_PUSH_INVALID_EXE_FILE = Arrays.asList( - "remote: error: invalid files", - "remote:", - "remote: hint: You have 1 invalid files in your Overleaf project:", - "remote: hint: file1.exe (invalid file extension)", - "To http://127.0.0.1:33872/" + PROJECT_ID, - "! [remote rejected] master -> master (invalid files)", - "error: failed to push some refs to 'http://127.0.0.1:33872/" + PROJECT_ID + "'" - ); - - @Test - public void pushSucceedsAfterRemovingInvalidFiles() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3872, getResource("/pushSucceedsAfterRemovingInvalidFiles").toFile()); - server.start(); - server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("invalidState")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33872, 3872) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33872, dir); - - // try to push invalid file; it should fail - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/invalidState/" + PROJECT_ID), testprojDir.toPath())); - assertEquals(0, runtime.exec("touch file1.exe", null, testprojDir).waitFor()); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - List actual = Util.linesFromStream(push.getErrorStream(), 0, "[K"); - assertEquals(EXPECTED_OUT_PUSH_INVALID_EXE_FILE, actual); - - // remove invalid file and push again; it should succeed this time - assertEquals(0, runtime.exec("git rm file1.exe", null, testprojDir).waitFor()); - gitCommit(testprojDir, "remove_invalid_file"); - server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("validState")); - gitPush(testprojDir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/validState/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void canServePushedFiles() throws IOException, ExecutionException, InterruptedException { - // - // I don't think we can test this completely without some changes to the mock server, because we have no way - // of pausing the test while the push is in progress. Once the push is over, the file isn't actually there for - // us to fetch any more. We can however test the access and error conditions, which comprise most of the logic. - // - int gitBridgePort = 33873; - int mockServerPort = 3873; - - server = new MockSnapshotServer( - mockServerPort, getResource("/canServePushedFiles").toFile()); - server.start(); - server.setState(states.get("canServePushedFiles").get("state")); - - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - - File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canServePushedFiles/state/" + PROJECT_ID), testprojDir.toPath())); - runtime.exec("touch push.tex", null, testprojDir).waitFor(); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - gitPush(testprojDir); - - // With no key, we should get a 404. - String url = "http://127.0.0.1:" + gitBridgePort + "/api/testproj/push.tex"; - Response response = asyncHttpClient().prepareGet(url).execute().get(); - assertEquals(404, response.getStatusCode()); - - // With an invalid project and no key, we should get a 404. - url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/push.tex"; - response = asyncHttpClient().prepareGet(url).execute().get(); - assertEquals(404, response.getStatusCode()); - - // With a bad key for a valid project, we should get a 404. - url = "http://127.0.0.1:" + gitBridgePort + "/api/testproj/push.tex?key=notavalidkey"; - response = asyncHttpClient().prepareGet(url).execute().get(); - assertEquals(404, response.getStatusCode()); - - // With a bad key for an invalid project, we should get a 404. - url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/push.tex?key=notavalidkey"; - response = asyncHttpClient().prepareGet(url).execute().get(); - assertEquals(404, response.getStatusCode()); - - } - - @Test - public void wlgbCanSwapProjects( - ) throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer( - 3874, - getResource("/wlgbCanSwapProjects").toFile() - ); - server.start(); - server.setState(states.get("wlgbCanSwapProjects").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33874, 3874, new SwapJobConfig(1, 0, 0, 250, null, true)) - }); - wlgb.run(); - File rootGitDir = new File(wlgb.config.getRootGitDirectory()); - File testProj1ServerDir = new File(rootGitDir, PROJECT_ID1); - File testProj2ServerDir = new File(rootGitDir, PROJECT_ID2); - File testProj1Dir = gitClone(PROJECT_ID1, 33874, dir); - assertTrue(testProj1ServerDir.exists()); - assertFalse(testProj2ServerDir.exists()); - gitClone(PROJECT_ID2, 33874, dir); - while (testProj1ServerDir.exists()); - assertFalse(testProj1ServerDir.exists()); - assertTrue(testProj2ServerDir.exists()); - FileUtils.deleteDirectory(testProj1Dir); - gitClone(PROJECT_ID1, 33874, dir); - while (testProj2ServerDir.exists()); - assertTrue(testProj1ServerDir.exists()); - assertFalse(testProj2ServerDir.exists()); - } - - private static final List EXPECTED_OUT_PUSH_SUBMODULE = Arrays.asList( - "remote: hint: Your Git repository contains a reference we cannot resolve.", - "remote: hint: If your project contains a Git submodule,", - "remote: hint: please remove it and try again.", - "To http://127.0.0.1:33875/" + PROJECT_ID, - "! [remote rejected] master -> master (invalid git repo)", - "error: failed to push some refs to 'http://127.0.0.1:33875/" + PROJECT_ID + "'" - ); - - @Test - public void pushSubmoduleFailsWithInvalidGitRepo() throws IOException, GitAPIException, InterruptedException { - server = new MockSnapshotServer(3875, getResource("/pushSubmoduleFailsWithInvalidGitRepo").toFile()); - server.start(); - server.setState(states.get("pushSubmoduleFailsWithInvalidGitRepo").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(33875, 3875) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, 33875, dir); - runtime.exec("mkdir sub", null, testprojDir).waitFor(); - File sub = new File(testprojDir, "sub"); - runtime.exec("touch sub.txt", null, sub).waitFor(); - gitInit(sub); - gitAdd(sub); - gitCommit(sub, "sub"); - gitAdd(testprojDir); - gitCommit(testprojDir, "push"); - Process push = gitPush(testprojDir, 1); - List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); - assertEquals(EXPECTED_OUT_PUSH_SUBMODULE, actual); - } - - @Test - public void usesCustomErrorHandler( - ) throws IOException, ExecutionException, InterruptedException { - - int gitBridgePort = 33873; - int mockServerPort = 3873; - - server = new MockSnapshotServer( - mockServerPort, getResource("/canServePushedFiles").toFile()); - server.start(); - server.setState(states.get("canServePushedFiles").get("state")); - - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - - // With an invalid project and no key, we should get a 404, - // which is rendered by our custom error handler. - String url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/main.tex"; - Response response = asyncHttpClient().prepareGet(url).execute().get(); - assertEquals(404, response.getStatusCode()); - assertEquals("{\"message\":\"HTTP error 404\"}", response.getResponseBody()); - - // With an unsupported URL outside the api, the request is assumed to - // be from a git client and we should get a 401 because the request - // does not include basic auth credentials. - url = "http://127.0.0.1:" + gitBridgePort + "/foo"; - response = asyncHttpClient().prepareGet(url).execute().get(); - assertEquals(401, response.getStatusCode()); - } - - @Test - public void cannotCloneAProtectedProjectWithoutAuthentication() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33883; - int mockServerPort = 3883; - - server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAProtectedProjectWithoutAuthentication").toFile()); - server.start(); - server.setState(states.get("cannotCloneAProtectedProjectWithoutAuthentication").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - - wlgb.run(); - Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, null, dir); - assertNotEquals(0, gitProcess.waitFor()); - } - - @Test - public void cannotCloneA4xxProject() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33879; - int mockServerPort = 3879; - - server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneA4xxProject").toFile()); - server.start(); - server.setState(states.get("cannotCloneA4xxProject").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - - wlgb.run(); - Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, null, dir); - assertNotEquals(0, gitProcess.waitFor()); - } - - @Test - public void cannotCloneAMissingProject() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33880; - int mockServerPort = 3880; - - server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAMissingProject").toFile()); - server.start(); - server.setState(states.get("cannotCloneAMissingProject").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - - wlgb.run(); - Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, null, dir); - assertNotEquals(0, gitProcess.waitFor()); - } - - @Test - public void canMigrateRepository() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33881; - int mockServerPort = 3881; - server = new MockSnapshotServer(mockServerPort, getResource("/canMigrateRepository").toFile()); - server.start(); - server.setState(states.get("canMigrateRepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir); - File testprojDir2 = gitClone(PROJECT_ID2, gitBridgePort, dir); - // Second project content is equal to content of the first - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canMigrateRepository/state/" + PROJECT_ID), testprojDir2.toPath())); - } - - @Test - public void skipMigrationWhenMigratedFromMissing() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33882; - int mockServerPort = 3882; - server = new MockSnapshotServer(mockServerPort, getResource("/skipMigrationWhenMigratedFromMissing").toFile()); - server.start(); - server.setState(states.get("skipMigrationWhenMigratedFromMissing").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - // don't clone the source project first - File testprojDir2 = gitClone(PROJECT_ID2, gitBridgePort, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/skipMigrationWhenMigratedFromMissing/state/" + PROJECT_ID2), testprojDir2.toPath())); - } - - @Test - public void canCloneAMigratedRepositoryWithoutChanges() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33883; - int mockServerPort = 3883; - server = new MockSnapshotServer(mockServerPort, getResource("/canCloneAMigratedRepositoryWithoutChanges").toFile()); - server.start(); - server.setState(states.get("canCloneAMigratedRepositoryWithoutChanges").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir); - assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneAMigratedRepositoryWithoutChanges/state/" + PROJECT_ID), testprojDir.toPath())); - } - - @Test - public void rejectV1Repository() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33884; - int mockServerPort = 3884; - server = new MockSnapshotServer(mockServerPort, getResource("/rejectV1Repository").toFile()); - server.start(); - server.setState(states.get("rejectV1Repository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/1234bbccddff.git", null, dir); - assertNotEquals(0, gitProcess.waitFor()); - } - - @Test - public void cannotCloneAHasDotGitProject() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33885; - int mockServerPort = 3885; - - server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAHasDotGitProject").toFile()); - server.start(); - server.setState(states.get("cannotCloneAHasDotGitProject").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - - wlgb.run(); - Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/conflict.git", null, dir); - assertNotEquals(0, gitProcess.waitFor()); - wlgb.stop(); - } - - @Test - public void cannotCloneProjectWithSlash() throws IOException, GitAPIException, InterruptedException { - int gitBridgePort = 33886; - int mockServerPort = 3886; - - server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); - server.start(); - server.setState(states.get("canCloneARepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - - wlgb.run(); - Process gitProcess = runtime.exec("git clone http://git:password@127.0.0.1:" + gitBridgePort + "/project/1234abcd", null, dir); - assertNotEquals(0, gitProcess.waitFor()); - - List actual = Util.linesFromStream(gitProcess.getErrorStream(), 0, ""); - assertEquals(Arrays.asList( + File repositoryDir = new File(dir, repositoryName); + gitConfig(repositoryDir); + return repositoryDir; + } + + private void gitInit(File dir) throws IOException, InterruptedException { + assertEquals(0, runtime.exec("git init", null, dir).waitFor()); + gitConfig(dir); + } + + private void gitAdd(File dir) throws IOException, InterruptedException { + assertEquals(0, runtime.exec("git add -A", null, dir).waitFor()); + } + + private void gitCommit(File dir, String msg) throws IOException, InterruptedException { + assertEquals(0, runtime.exec("git commit -m \"" + msg + "\"", null, dir).waitFor()); + } + + private Process gitPush(File dir) throws IOException, InterruptedException { + return gitPush(dir, 0); + } + + private Process gitPush(File dir, int exit) throws IOException, InterruptedException { + Process ret = runtime.exec("git push", null, dir); + assertEquals(exit, ret.waitFor()); + return ret; + } + + private void gitPull(File dir) throws IOException, InterruptedException { + assertEquals(0, runtime.exec("git pull", null, dir).waitFor()); + } + + @Test + public void canCloneARepository() throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3857, getResource("/canCloneARepository").toFile()); + server.start(); + server.setState(states.get("canCloneARepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33857, 3857)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33857, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canCloneARepository/state/" + PROJECT_ID), testprojDir.toPath())); + } + + @Test + public void canCloneMultipleRepositories() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3858, getResource("/canCloneMultipleRepositories").toFile()); + server.start(); + server.setState(states.get("canCloneMultipleRepositories").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33858, 3858)}); + wlgb.run(); + File testproj1Dir = gitClone(PROJECT_ID1, 33858, dir); + File testproj2Dir = gitClone(PROJECT_ID2, 33858, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canCloneMultipleRepositories/state/" + PROJECT_ID1), + testproj1Dir.toPath())); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canCloneMultipleRepositories/state/" + PROJECT_ID2), + testproj2Dir.toPath())); + } + + @Test + public void canPullAModifiedTexFile() throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3859, getResource("/canPullAModifiedTexFile").toFile()); + server.start(); + server.setState(states.get("canPullAModifiedTexFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33859, 3859)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33859, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullAModifiedTexFile/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullAModifiedTexFile").get("withModifiedTexFile")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullAModifiedTexFile/withModifiedTexFile/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullADeletedTexFile() throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3860, getResource("/canPullADeletedTexFile").toFile()); + server.start(); + server.setState(states.get("canPullADeletedTexFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33860, 3860)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33860, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullADeletedTexFile/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullADeletedTexFile").get("withDeletedTexFile")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullADeletedTexFile/withDeletedTexFile/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullAModifiedBinaryFile() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3862, getResource("/canPullAModifiedBinaryFile").toFile()); + server.start(); + server.setState(states.get("canPullAModifiedBinaryFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33862, 3862)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33862, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullAModifiedBinaryFile/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullAModifiedBinaryFile").get("withModifiedBinaryFile")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullAModifiedBinaryFile/withModifiedBinaryFile/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullADeletedBinaryFile() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3863, getResource("/canPullADeletedBinaryFile").toFile()); + server.start(); + server.setState(states.get("canPullADeletedBinaryFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33863, 3863)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33863, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullADeletedBinaryFile/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullADeletedBinaryFile").get("withDeletedBinaryFile")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullADeletedBinaryFile/withDeletedBinaryFile/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullADuplicateBinaryFile() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(4001, getResource("/canPullADuplicateBinaryFile").toFile()); + server.start(); + server.setState(states.get("canPullADuplicateBinaryFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(44001, 4001)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 44001, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullADuplicateBinaryFile/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullADuplicateBinaryFile").get("withDuplicateBinaryFile")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canCloneDuplicateBinaryFiles() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(4002, getResource("/canCloneDuplicateBinaryFiles").toFile()); + server.start(); + server.setState(states.get("canCloneDuplicateBinaryFiles").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(44002, 4002)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 44002, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canCloneDuplicateBinaryFiles/state/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullUpdatedBinaryFiles() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(4003, getResource("/canPullUpdatedBinaryFiles").toFile()); + server.start(); + server.setState(states.get("canPullUpdatedBinaryFiles").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(44003, 4003)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 44003, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullUpdatedBinaryFiles/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullUpdatedBinaryFiles").get("withUpdatedBinaryFiles")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullAModifiedNestedFile() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3864, getResource("/canPullAModifiedNestedFile").toFile()); + server.start(); + server.setState(states.get("canPullAModifiedNestedFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33864, 3864)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33864, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullAModifiedNestedFile/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullAModifiedNestedFile").get("withModifiedNestedFile")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullAModifiedNestedFile/withModifiedNestedFile/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPullDeletedNestedFiles() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3865, getResource("/canPullDeletedNestedFiles").toFile()); + server.start(); + server.setState(states.get("canPullDeletedNestedFiles").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33865, 3865)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33865, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullDeletedNestedFiles/base/" + PROJECT_ID), testprojDir.toPath())); + server.setState(states.get("canPullDeletedNestedFiles").get("withDeletedNestedFiles")); + gitPull(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPullDeletedNestedFiles/withDeletedNestedFiles/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canPushFilesSuccessfully() throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3866, getResource("/canPushFilesSuccessfully").toFile()); + server.start(); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33866, 3866)}); + wlgb.run(); + server.setState(states.get("canPushFilesSuccessfully").get("state")); + File testprojDir = gitClone(PROJECT_ID, 33866, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canPushFilesSuccessfully/state/" + PROJECT_ID), testprojDir.toPath())); + assertEquals(0, runtime.exec("touch push.tex", null, testprojDir).waitFor()); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + gitPush(testprojDir); + } + + private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST = + "error: failed to push some refs to 'http://127.0.0.1:33867/" + + PROJECT_ID + + "'\n" + + "hint: Updates were rejected because the tip of your current branch is behind\n" + + "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + + "hint: 'git pull ...') before pushing again.\n" + + "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n"; + + @Test + public void pushFailsOnFirstStageOutOfDate() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3867, getResource("/pushFailsOnFirstStageOutOfDate").toFile()); + server.start(); + server.setState(states.get("pushFailsOnFirstStageOutOfDate").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33867, 3867)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33867, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushFailsOnFirstStageOutOfDate/state/" + PROJECT_ID), + testprojDir.toPath())); + runtime.exec("touch push.tex", null, testprojDir).waitFor(); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST, Util.fromStream(push.getErrorStream(), 2)); + } + + private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND = + "error: failed to push some refs to 'http://127.0.0.1:33868/" + + PROJECT_ID + + "'\n" + + "hint: Updates were rejected because the tip of your current branch is behind\n" + + "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + + "hint: 'git pull ...') before pushing again.\n" + + "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n"; + + @Test + public void pushFailsOnSecondStageOutOfDate() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3868, getResource("/pushFailsOnSecondStageOutOfDate").toFile()); + server.start(); + server.setState(states.get("pushFailsOnSecondStageOutOfDate").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33868, 3868)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33868, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushFailsOnSecondStageOutOfDate/state/" + PROJECT_ID), + testprojDir.toPath())); + runtime.exec("touch push.tex", null, testprojDir).waitFor(); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND, Util.fromStream(push.getErrorStream(), 2)); + } + + private static final List EXPECTED_OUT_PUSH_INVALID_FILES = + Arrays.asList( + "remote: hint: You have 4 invalid files in your Overleaf project:", + "remote: hint: file1.invalid (error)", + "remote: hint: file2.exe (invalid file extension)", + "remote: hint: hello world.png (rename to: hello_world.png)", + "remote: hint: an image.jpg (rename to: an_image.jpg)", + "To http://127.0.0.1:33869/" + PROJECT_ID, + "! [remote rejected] master -> master (invalid files)", + "error: failed to push some refs to 'http://127.0.0.1:33869/" + PROJECT_ID + "'"); + + @Test + public void pushFailsOnInvalidFiles() throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3869, getResource("/pushFailsOnInvalidFiles").toFile()); + server.start(); + server.setState(states.get("pushFailsOnInvalidFiles").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33869, 3869)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33869, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushFailsOnInvalidFiles/state/" + PROJECT_ID), testprojDir.toPath())); + runtime.exec("touch push.tex", null, testprojDir).waitFor(); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); + assertEquals(EXPECTED_OUT_PUSH_INVALID_FILES, actual); + } + + private static final List EXPECTED_OUT_PUSH_INVALID_PROJECT = + Arrays.asList( + "remote: hint: project: no main file", + "remote: hint: The project would have no (editable) main .tex file.", + "To http://127.0.0.1:33870/" + PROJECT_ID, + "! [remote rejected] master -> master (invalid project)", + "error: failed to push some refs to 'http://127.0.0.1:33870/" + PROJECT_ID + "'"); + + @Test + public void pushFailsOnInvalidProject() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3870, getResource("/pushFailsOnInvalidProject").toFile()); + server.start(); + server.setState(states.get("pushFailsOnInvalidProject").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33870, 3870)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33870, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushFailsOnInvalidProject/state/" + PROJECT_ID), testprojDir.toPath())); + runtime.exec("touch push.tex", null, testprojDir).waitFor(); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); + assertEquals(EXPECTED_OUT_PUSH_INVALID_PROJECT, actual); + } + + private static final List EXPECTED_OUT_PUSH_UNEXPECTED_ERROR = + Arrays.asList( + "remote: hint: There was an internal error with the Overleaf server.", + "remote: hint: Please contact Overleaf.", + "To http://127.0.0.1:33871/" + PROJECT_ID, + "! [remote rejected] master -> master (Overleaf error)", + "error: failed to push some refs to 'http://127.0.0.1:33871/" + PROJECT_ID + "'"); + + /* this one prints a stack trace */ + @Test + public void pushFailsOnUnexpectedError() + throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3871, getResource("/pushFailsOnUnexpectedError").toFile()); + server.start(); + server.setState(states.get("pushFailsOnUnexpectedError").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33871, 3871)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33871, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushFailsOnUnexpectedError/state/" + PROJECT_ID), testprojDir.toPath())); + runtime.exec("touch push.tex", null, testprojDir).waitFor(); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); + assertEquals(EXPECTED_OUT_PUSH_UNEXPECTED_ERROR, actual); + } + + private static final List EXPECTED_OUT_PUSH_INVALID_EXE_FILE = + Arrays.asList( + "remote: error: invalid files", + "remote:", + "remote: hint: You have 1 invalid files in your Overleaf project:", + "remote: hint: file1.exe (invalid file extension)", + "To http://127.0.0.1:33872/" + PROJECT_ID, + "! [remote rejected] master -> master (invalid files)", + "error: failed to push some refs to 'http://127.0.0.1:33872/" + PROJECT_ID + "'"); + + @Test + public void pushSucceedsAfterRemovingInvalidFiles() + throws IOException, GitAPIException, InterruptedException { + server = + new MockSnapshotServer( + 3872, getResource("/pushSucceedsAfterRemovingInvalidFiles").toFile()); + server.start(); + server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("invalidState")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33872, 3872)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33872, dir); + + // try to push invalid file; it should fail + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushSucceedsAfterRemovingInvalidFiles/invalidState/" + PROJECT_ID), + testprojDir.toPath())); + assertEquals(0, runtime.exec("touch file1.exe", null, testprojDir).waitFor()); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + List actual = Util.linesFromStream(push.getErrorStream(), 0, "[K"); + assertEquals(EXPECTED_OUT_PUSH_INVALID_EXE_FILE, actual); + + // remove invalid file and push again; it should succeed this time + assertEquals(0, runtime.exec("git rm file1.exe", null, testprojDir).waitFor()); + gitCommit(testprojDir, "remove_invalid_file"); + server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("validState")); + gitPush(testprojDir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/pushSucceedsAfterRemovingInvalidFiles/validState/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void canServePushedFiles() throws IOException, ExecutionException, InterruptedException { + // + // I don't think we can test this completely without some changes to the mock server, because we + // have no way + // of pausing the test while the push is in progress. Once the push is over, the file isn't + // actually there for + // us to fetch any more. We can however test the access and error conditions, which comprise + // most of the logic. + // + int gitBridgePort = 33873; + int mockServerPort = 3873; + + server = new MockSnapshotServer(mockServerPort, getResource("/canServePushedFiles").toFile()); + server.start(); + server.setState(states.get("canServePushedFiles").get("state")); + + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + + File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canServePushedFiles/state/" + PROJECT_ID), testprojDir.toPath())); + runtime.exec("touch push.tex", null, testprojDir).waitFor(); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + gitPush(testprojDir); + + // With no key, we should get a 404. + String url = "http://127.0.0.1:" + gitBridgePort + "/api/testproj/push.tex"; + Response response = asyncHttpClient().prepareGet(url).execute().get(); + assertEquals(404, response.getStatusCode()); + + // With an invalid project and no key, we should get a 404. + url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/push.tex"; + response = asyncHttpClient().prepareGet(url).execute().get(); + assertEquals(404, response.getStatusCode()); + + // With a bad key for a valid project, we should get a 404. + url = "http://127.0.0.1:" + gitBridgePort + "/api/testproj/push.tex?key=notavalidkey"; + response = asyncHttpClient().prepareGet(url).execute().get(); + assertEquals(404, response.getStatusCode()); + + // With a bad key for an invalid project, we should get a 404. + url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/push.tex?key=notavalidkey"; + response = asyncHttpClient().prepareGet(url).execute().get(); + assertEquals(404, response.getStatusCode()); + } + + @Test + public void wlgbCanSwapProjects() throws IOException, GitAPIException, InterruptedException { + server = new MockSnapshotServer(3874, getResource("/wlgbCanSwapProjects").toFile()); + server.start(); + server.setState(states.get("wlgbCanSwapProjects").get("state")); + wlgb = + new GitBridgeApp( + new String[] { + makeConfigFile(33874, 3874, new SwapJobConfig(1, 0, 0, 250, null, true)) + }); + wlgb.run(); + File rootGitDir = new File(wlgb.config.getRootGitDirectory()); + File testProj1ServerDir = new File(rootGitDir, PROJECT_ID1); + File testProj2ServerDir = new File(rootGitDir, PROJECT_ID2); + File testProj1Dir = gitClone(PROJECT_ID1, 33874, dir); + assertTrue(testProj1ServerDir.exists()); + assertFalse(testProj2ServerDir.exists()); + gitClone(PROJECT_ID2, 33874, dir); + while (testProj1ServerDir.exists()) + ; + assertFalse(testProj1ServerDir.exists()); + assertTrue(testProj2ServerDir.exists()); + FileUtils.deleteDirectory(testProj1Dir); + gitClone(PROJECT_ID1, 33874, dir); + while (testProj2ServerDir.exists()) + ; + assertTrue(testProj1ServerDir.exists()); + assertFalse(testProj2ServerDir.exists()); + } + + private static final List EXPECTED_OUT_PUSH_SUBMODULE = + Arrays.asList( + "remote: hint: Your Git repository contains a reference we cannot resolve.", + "remote: hint: If your project contains a Git submodule,", + "remote: hint: please remove it and try again.", + "To http://127.0.0.1:33875/" + PROJECT_ID, + "! [remote rejected] master -> master (invalid git repo)", + "error: failed to push some refs to 'http://127.0.0.1:33875/" + PROJECT_ID + "'"); + + @Test + public void pushSubmoduleFailsWithInvalidGitRepo() + throws IOException, GitAPIException, InterruptedException { + server = + new MockSnapshotServer(3875, getResource("/pushSubmoduleFailsWithInvalidGitRepo").toFile()); + server.start(); + server.setState(states.get("pushSubmoduleFailsWithInvalidGitRepo").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(33875, 3875)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, 33875, dir); + runtime.exec("mkdir sub", null, testprojDir).waitFor(); + File sub = new File(testprojDir, "sub"); + runtime.exec("touch sub.txt", null, sub).waitFor(); + gitInit(sub); + gitAdd(sub); + gitCommit(sub, "sub"); + gitAdd(testprojDir); + gitCommit(testprojDir, "push"); + Process push = gitPush(testprojDir, 1); + List actual = Util.linesFromStream(push.getErrorStream(), 2, "[K"); + assertEquals(EXPECTED_OUT_PUSH_SUBMODULE, actual); + } + + @Test + public void usesCustomErrorHandler() + throws IOException, ExecutionException, InterruptedException { + + int gitBridgePort = 33873; + int mockServerPort = 3873; + + server = new MockSnapshotServer(mockServerPort, getResource("/canServePushedFiles").toFile()); + server.start(); + server.setState(states.get("canServePushedFiles").get("state")); + + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + + // With an invalid project and no key, we should get a 404, + // which is rendered by our custom error handler. + String url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/main.tex"; + Response response = asyncHttpClient().prepareGet(url).execute().get(); + assertEquals(404, response.getStatusCode()); + assertEquals("{\"message\":\"HTTP error 404\"}", response.getResponseBody()); + + // With an unsupported URL outside the api, the request is assumed to + // be from a git client and we should get a 401 because the request + // does not include basic auth credentials. + url = "http://127.0.0.1:" + gitBridgePort + "/foo"; + response = asyncHttpClient().prepareGet(url).execute().get(); + assertEquals(401, response.getStatusCode()); + } + + @Test + public void cannotCloneAProtectedProjectWithoutAuthentication() + throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33883; + int mockServerPort = 3883; + + server = + new MockSnapshotServer( + mockServerPort, + getResource("/cannotCloneAProtectedProjectWithoutAuthentication").toFile()); + server.start(); + server.setState(states.get("cannotCloneAProtectedProjectWithoutAuthentication").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + + wlgb.run(); + Process gitProcess = + runtime.exec( + "git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, + null, + dir); + assertNotEquals(0, gitProcess.waitFor()); + } + + @Test + public void cannotCloneA4xxProject() throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33879; + int mockServerPort = 3879; + + server = + new MockSnapshotServer(mockServerPort, getResource("/cannotCloneA4xxProject").toFile()); + server.start(); + server.setState(states.get("cannotCloneA4xxProject").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + + wlgb.run(); + Process gitProcess = + runtime.exec( + "git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, + null, + dir); + assertNotEquals(0, gitProcess.waitFor()); + } + + @Test + public void cannotCloneAMissingProject() + throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33880; + int mockServerPort = 3880; + + server = + new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAMissingProject").toFile()); + server.start(); + server.setState(states.get("cannotCloneAMissingProject").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + + wlgb.run(); + Process gitProcess = + runtime.exec( + "git clone http://git:password@127.0.0.1:" + gitBridgePort + "/" + PROJECT_ID, + null, + dir); + assertNotEquals(0, gitProcess.waitFor()); + } + + @Test + public void canMigrateRepository() throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33881; + int mockServerPort = 3881; + server = new MockSnapshotServer(mockServerPort, getResource("/canMigrateRepository").toFile()); + server.start(); + server.setState(states.get("canMigrateRepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir); + File testprojDir2 = gitClone(PROJECT_ID2, gitBridgePort, dir); + // Second project content is equal to content of the first + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canMigrateRepository/state/" + PROJECT_ID), testprojDir2.toPath())); + } + + @Test + public void skipMigrationWhenMigratedFromMissing() + throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33882; + int mockServerPort = 3882; + server = + new MockSnapshotServer( + mockServerPort, getResource("/skipMigrationWhenMigratedFromMissing").toFile()); + server.start(); + server.setState(states.get("skipMigrationWhenMigratedFromMissing").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + // don't clone the source project first + File testprojDir2 = gitClone(PROJECT_ID2, gitBridgePort, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/skipMigrationWhenMigratedFromMissing/state/" + PROJECT_ID2), + testprojDir2.toPath())); + } + + @Test + public void canCloneAMigratedRepositoryWithoutChanges() + throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33883; + int mockServerPort = 3883; + server = + new MockSnapshotServer( + mockServerPort, getResource("/canCloneAMigratedRepositoryWithoutChanges").toFile()); + server.start(); + server.setState(states.get("canCloneAMigratedRepositoryWithoutChanges").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + File testprojDir = gitClone(PROJECT_ID, gitBridgePort, dir); + assertTrue( + FileUtil.gitDirectoriesAreEqual( + getResource("/canCloneAMigratedRepositoryWithoutChanges/state/" + PROJECT_ID), + testprojDir.toPath())); + } + + @Test + public void rejectV1Repository() throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33884; + int mockServerPort = 3884; + server = new MockSnapshotServer(mockServerPort, getResource("/rejectV1Repository").toFile()); + server.start(); + server.setState(states.get("rejectV1Repository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + Process gitProcess = + runtime.exec( + "git clone http://git:password@127.0.0.1:" + gitBridgePort + "/1234bbccddff.git", + null, + dir); + assertNotEquals(0, gitProcess.waitFor()); + } + + @Test + public void cannotCloneAHasDotGitProject() + throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33885; + int mockServerPort = 3885; + + server = + new MockSnapshotServer( + mockServerPort, getResource("/cannotCloneAHasDotGitProject").toFile()); + server.start(); + server.setState(states.get("cannotCloneAHasDotGitProject").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + + wlgb.run(); + Process gitProcess = + runtime.exec( + "git clone http://git:password@127.0.0.1:" + gitBridgePort + "/conflict.git", + null, + dir); + assertNotEquals(0, gitProcess.waitFor()); + wlgb.stop(); + } + + @Test + public void cannotCloneProjectWithSlash() + throws IOException, GitAPIException, InterruptedException { + int gitBridgePort = 33886; + int mockServerPort = 3886; + + server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); + server.start(); + server.setState(states.get("canCloneARepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + + wlgb.run(); + Process gitProcess = + runtime.exec( + "git clone http://git:password@127.0.0.1:" + gitBridgePort + "/project/1234abcd", + null, + dir); + assertNotEquals(0, gitProcess.waitFor()); + + List actual = Util.linesFromStream(gitProcess.getErrorStream(), 0, ""); + assertEquals( + Arrays.asList( "Cloning into '1234abcd'...", "remote: Invalid Project ID (must not have a '/project' prefix)", - "fatal: repository 'http://127.0.0.1:33886/project/1234abcd/' not found" - ), actual); + "fatal: repository 'http://127.0.0.1:33886/project/1234abcd/' not found"), + actual); - wlgb.stop(); + wlgb.stop(); + } + + @Test + public void testStatusAndHealthCheckEndpoints() throws ClientProtocolException, IOException { + int gitBridgePort = 33887; + int mockServerPort = 3887; + server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); + server.start(); + server.setState(states.get("canCloneARepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + HttpClient client = HttpClients.createDefault(); + String urlBase = "http://127.0.0.1:" + gitBridgePort; + // Status + HttpGet statusRequest = new HttpGet(urlBase + "/status"); + HttpResponse statusResponse = client.execute(statusRequest); + assertEquals(200, statusResponse.getStatusLine().getStatusCode()); + // Health Check + HttpGet healthCheckRequest = new HttpGet(urlBase + "/health_check"); + HttpResponse healthCheckResponse = client.execute(healthCheckRequest); + assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode()); + } + + @Test + public void testStatusAndHealthCheckEndpointsWithTrailingSlash() + throws ClientProtocolException, IOException { + int gitBridgePort = 33888; + int mockServerPort = 3888; + server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); + server.start(); + server.setState(states.get("canCloneARepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + HttpClient client = HttpClients.createDefault(); + String urlBase = "http://127.0.0.1:" + gitBridgePort; + // Status + HttpGet statusRequest = new HttpGet(urlBase + "/status/"); + HttpResponse statusResponse = client.execute(statusRequest); + assertEquals(200, statusResponse.getStatusLine().getStatusCode()); + // Health Check + HttpGet healthCheckRequest = new HttpGet(urlBase + "/health_check/"); + HttpResponse healthCheckResponse = client.execute(healthCheckRequest); + assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode()); + } + + @Test + public void testStatusAndHealthCheckEndpointsWithHead() + throws ClientProtocolException, IOException { + int gitBridgePort = 33889; + int mockServerPort = 3889; + server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); + server.start(); + server.setState(states.get("canCloneARepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + HttpClient client = HttpClients.createDefault(); + String urlBase = "http://127.0.0.1:" + gitBridgePort; + // Status + HttpHead statusRequest = new HttpHead(urlBase + "/status"); + HttpResponse statusResponse = client.execute(statusRequest); + assertEquals(200, statusResponse.getStatusLine().getStatusCode()); + // Health Check + HttpHead healthCheckRequest = new HttpHead(urlBase + "/health_check"); + HttpResponse healthCheckResponse = client.execute(healthCheckRequest); + assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode()); + } + + @Test + public void gitLfsBatchEndpoint() throws ClientProtocolException, IOException, ParseException { + int gitBridgePort = 33890; + int mockServerPort = 3890; + server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); + server.start(); + server.setState(states.get("canCloneARepository").get("state")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + HttpClient client = HttpClients.createDefault(); + String urlBase = "http://git:password@127.0.0.1:" + gitBridgePort; + HttpPost gitLfsRequest = + new HttpPost(urlBase + "/5f2419407929eb0026641967.git/info/lfs/objects/batch"); + HttpResponse gitLfsResponse = client.execute(gitLfsRequest); + assertEquals(422, gitLfsResponse.getStatusLine().getStatusCode()); + HttpEntity entity = gitLfsResponse.getEntity(); + String responseString = EntityUtils.toString(entity, "UTF-8"); + assertTrue(responseString.contains("Git LFS is not supported on Overleaf")); + } + + @Test + public void canPullIgnoredForceAddedFile() throws IOException, InterruptedException { + int gitBridgePort = 33891; + int mockServerPort = 3891; + server = + new MockSnapshotServer( + mockServerPort, getResource("/canPullIgnoredForceAddedFile").toFile()); + server.start(); + server.setState(states.get("canPullIgnoredForceAddedFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + File testProjDir = gitClone(PROJECT_ID, gitBridgePort, dir); + File one = new File(testProjDir, "sub/one.txt"); + one.createNewFile(); + FileWriter fw = new FileWriter(one.getPath()); + fw.write("1"); + fw.close(); + assertEquals(0, runtime.exec("git add -A -f", null, testProjDir).waitFor()); + gitCommit(testProjDir, "push"); + gitPush(testProjDir); + server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile")); + gitPull(testProjDir); + File f = new File(testProjDir.getPath() + "/sub/one.txt"); + assertTrue(f.exists()); + } + + @Test + public void canPullIgnoredFileFromOverleaf() throws IOException, InterruptedException { + int gitBridgePort = 33892; + int mockServerPort = 3892; + server = + new MockSnapshotServer( + mockServerPort, getResource("/canPullIgnoredForceAddedFile").toFile()); + server.start(); + server.setState(states.get("canPullIgnoredForceAddedFile").get("base")); + wlgb = new GitBridgeApp(new String[] {makeConfigFile(gitBridgePort, mockServerPort)}); + wlgb.run(); + File testProjDir = gitClone(PROJECT_ID, gitBridgePort, dir); + server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile")); + gitPull(testProjDir); + File f = new File(testProjDir.getPath() + "/sub/one.txt"); + assertTrue(f.exists()); + } + + private String makeConfigFile(int port, int apiPort) throws IOException { + return makeConfigFile(port, apiPort, null); + } + + private String makeConfigFile(int port, int apiPort, SwapJobConfig swapCfg) throws IOException { + File wlgb = folder.newFolder(); + File config = folder.newFile(); + PrintWriter writer = new PrintWriter(config); + String cfgStr = + "{\n" + + " \"port\": " + + port + + ",\n" + + " \"bindIp\": \"127.0.0.1\",\n" + + " \"idleTimeout\": 30000,\n" + + " \"rootGitDirectory\": \"" + + wlgb.getAbsolutePath() + + "\",\n" + + " \"apiBaseUrl\": \"http://127.0.0.1:" + + apiPort + + "/api/v0\",\n" + + " \"postbackBaseUrl\": \"http://127.0.0.1:" + + port + + "\",\n" + + " \"serviceName\": \"Overleaf\",\n" + + " \"oauth2\": {\n" + + " \"oauth2ClientID\": \"clientID\",\n" + + " \"oauth2ClientSecret\": \"oauth2 client secret\",\n" + + " \"oauth2Server\": \"http://127.0.0.1:" + + apiPort + + "\"\n" + + " }"; + if (swapCfg != null) { + cfgStr += + ",\n" + + " \"swapStore\": {\n" + + " \"type\": \"memory\",\n" + + " \"awsAccessKey\": null,\n" + + " \"awsSecret\": null,\n" + + " \"s3BucketName\": \"com.overleaf.testbucket\"\n" + + " },\n" + + " \"swapJob\": {\n" + + " \"allowUnsafeStores\": true," + + " \"minProjects\": " + + swapCfg.getMinProjects() + + ",\n" + + " \"lowGiB\": " + + swapCfg.getLowGiB() + + ",\n" + + " \"highGiB\": " + + swapCfg.getHighGiB() + + ",\n" + + " \"intervalMillis\": " + + swapCfg.getIntervalMillis() + + "\n" + + " }\n"; } + cfgStr += "}\n"; + writer.print(cfgStr); + writer.close(); + return config.getAbsolutePath(); + } - @Test - public void testStatusAndHealthCheckEndpoints() throws ClientProtocolException, IOException { - int gitBridgePort = 33887; - int mockServerPort = 3887; - server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); - server.start(); - server.setState(states.get("canCloneARepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - HttpClient client = HttpClients.createDefault(); - String urlBase = "http://127.0.0.1:" + gitBridgePort; - // Status - HttpGet statusRequest = new HttpGet(urlBase+"/status"); - HttpResponse statusResponse = client.execute(statusRequest); - assertEquals(200, statusResponse.getStatusLine().getStatusCode()); - // Health Check - HttpGet healthCheckRequest = new HttpGet(urlBase+"/health_check"); - HttpResponse healthCheckResponse = client.execute(healthCheckRequest); - assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode()); - } - - @Test - public void testStatusAndHealthCheckEndpointsWithTrailingSlash() throws ClientProtocolException, IOException { - int gitBridgePort = 33888; - int mockServerPort = 3888; - server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); - server.start(); - server.setState(states.get("canCloneARepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - HttpClient client = HttpClients.createDefault(); - String urlBase = "http://127.0.0.1:" + gitBridgePort; - // Status - HttpGet statusRequest = new HttpGet(urlBase+"/status/"); - HttpResponse statusResponse = client.execute(statusRequest); - assertEquals(200, statusResponse.getStatusLine().getStatusCode()); - // Health Check - HttpGet healthCheckRequest = new HttpGet(urlBase+"/health_check/"); - HttpResponse healthCheckResponse = client.execute(healthCheckRequest); - assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode()); - } - - @Test - public void testStatusAndHealthCheckEndpointsWithHead() throws ClientProtocolException, IOException { - int gitBridgePort = 33889; - int mockServerPort = 3889; - server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); - server.start(); - server.setState(states.get("canCloneARepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - HttpClient client = HttpClients.createDefault(); - String urlBase = "http://127.0.0.1:" + gitBridgePort; - // Status - HttpHead statusRequest = new HttpHead(urlBase+"/status"); - HttpResponse statusResponse = client.execute(statusRequest); - assertEquals(200, statusResponse.getStatusLine().getStatusCode()); - // Health Check - HttpHead healthCheckRequest = new HttpHead(urlBase+"/health_check"); - HttpResponse healthCheckResponse = client.execute(healthCheckRequest); - assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode()); - } - - @Test - public void gitLfsBatchEndpoint() throws ClientProtocolException, IOException, ParseException { - int gitBridgePort = 33890; - int mockServerPort = 3890; - server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile()); - server.start(); - server.setState(states.get("canCloneARepository").get("state")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - HttpClient client = HttpClients.createDefault(); - String urlBase = "http://git:password@127.0.0.1:" + gitBridgePort; - HttpPost gitLfsRequest = new HttpPost(urlBase+"/5f2419407929eb0026641967.git/info/lfs/objects/batch"); - HttpResponse gitLfsResponse = client.execute(gitLfsRequest); - assertEquals(422, gitLfsResponse.getStatusLine().getStatusCode()); - HttpEntity entity = gitLfsResponse.getEntity(); - String responseString = EntityUtils.toString(entity, "UTF-8"); - assertTrue(responseString.contains("Git LFS is not supported on Overleaf")); - } - - @Test - public void canPullIgnoredForceAddedFile() throws IOException, InterruptedException { - int gitBridgePort = 33891; - int mockServerPort = 3891; - server = new MockSnapshotServer(mockServerPort, getResource("/canPullIgnoredForceAddedFile").toFile()); - server.start(); - server.setState(states.get("canPullIgnoredForceAddedFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - File testProjDir = gitClone(PROJECT_ID, gitBridgePort, dir); - File one = new File(testProjDir, "sub/one.txt"); - one.createNewFile(); - FileWriter fw = new FileWriter(one.getPath()); - fw.write("1"); - fw.close(); - assertEquals(0, runtime.exec( - "git add -A -f", null, testProjDir - ).waitFor()); - gitCommit(testProjDir, "push"); - gitPush(testProjDir); - server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile")); - gitPull(testProjDir); - File f = new File(testProjDir.getPath() + "/sub/one.txt"); - assertTrue(f.exists()); - } - - @Test - public void canPullIgnoredFileFromOverleaf() throws IOException, InterruptedException { - int gitBridgePort = 33892; - int mockServerPort = 3892; - server = new MockSnapshotServer(mockServerPort, getResource("/canPullIgnoredForceAddedFile").toFile()); - server.start(); - server.setState(states.get("canPullIgnoredForceAddedFile").get("base")); - wlgb = new GitBridgeApp(new String[] { - makeConfigFile(gitBridgePort, mockServerPort) - }); - wlgb.run(); - File testProjDir = gitClone(PROJECT_ID, gitBridgePort, dir); - server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile")); - gitPull(testProjDir); - File f = new File(testProjDir.getPath() + "/sub/one.txt"); - assertTrue(f.exists()); - } - - private String makeConfigFile( - int port, - int apiPort - ) throws IOException { - return makeConfigFile(port, apiPort, null); - } - - private String makeConfigFile( - int port, - int apiPort, - SwapJobConfig swapCfg - ) throws IOException { - File wlgb = folder.newFolder(); - File config = folder.newFile(); - PrintWriter writer = new PrintWriter(config); - String cfgStr = - "{\n" + - " \"port\": " + port + ",\n" + - " \"bindIp\": \"127.0.0.1\",\n" + - " \"idleTimeout\": 30000,\n" + - " \"rootGitDirectory\": \"" + - wlgb.getAbsolutePath() + - "\",\n" + - " \"apiBaseUrl\": \"http://127.0.0.1:" + - apiPort + - "/api/v0\",\n" + - " \"postbackBaseUrl\": \"http://127.0.0.1:" + - port + - "\",\n" + - " \"serviceName\": \"Overleaf\",\n" + - " \"oauth2\": {\n" + - " \"oauth2ClientID\": \"clientID\",\n" + - " \"oauth2ClientSecret\": \"oauth2 client secret\",\n" + - " \"oauth2Server\": \"http://127.0.0.1:" + apiPort + "\"\n" + - " }"; - if (swapCfg != null) { - cfgStr += ",\n" + - " \"swapStore\": {\n" + - " \"type\": \"memory\",\n" + - " \"awsAccessKey\": null,\n" + - " \"awsSecret\": null,\n" + - " \"s3BucketName\": \"com.overleaf.testbucket\"\n" + - " },\n" + - " \"swapJob\": {\n" + - " \"allowUnsafeStores\": true," + - " \"minProjects\": " + - swapCfg.getMinProjects() + - ",\n" + - " \"lowGiB\": " + - swapCfg.getLowGiB() + - ",\n" + - " \"highGiB\": " + - swapCfg.getHighGiB() + - ",\n" + - " \"intervalMillis\": " + - swapCfg.getIntervalMillis() + - "\n" + - " }\n"; - } - cfgStr += "}\n"; - writer.print(cfgStr); - writer.close(); - return config.getAbsolutePath(); - } - - private Path getResource(String path) { - return Paths.get( - "src/test/resources/" + - "uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest" + path - ); - } - - private InputStream getResourceAsStream(String path) { - try { - return new FileInputStream(getResource(path).toFile()); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } - - private static String withoutWhitespace(String s) { - return s.replaceAll("\\s",""); + private Path getResource(String path) { + return Paths.get( + "src/test/resources/" + "uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest" + path); + } + + private InputStream getResourceAsStream(String path) { + try { + return new FileInputStream(getResource(path).toFile()); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); } + } + private static String withoutWhitespace(String s) { + return s.replaceAll("\\s", ""); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/config/ConfigTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/config/ConfigTest.java index 90d1606967..ddafc621d6 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/config/ConfigTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/config/ConfigTest.java @@ -1,108 +1,110 @@ package uk.ac.ic.wlgitbridge.application.config; -import org.junit.Test; - -import java.io.Reader; -import java.io.StringReader; - import static org.junit.Assert.*; -/** +import java.io.Reader; +import java.io.StringReader; +import org.junit.Test; + +/* * Created by winston on 25/10/15. */ public class ConfigTest { - @Test - public void testConstructWithOauth() { - Reader reader = new StringReader("{\n" + - " \"port\": 80,\n" + - " \"bindIp\": \"127.0.0.1\",\n" + - " \"idleTimeout\": 30000,\n" + - " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + - " \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" + - " \"postbackBaseUrl\": \"http://127.0.0.1\",\n" + - " \"serviceName\": \"Overleaf\",\n" + - " \"oauth2\": {\n" + - " \"oauth2ClientID\": \"clientID\",\n" + - " \"oauth2ClientSecret\": \"oauth2 client secret\",\n" + - " \"oauth2Server\": \"https://www.overleaf.com\"\n" + - " }\n" + - "}\n"); - Config config = new Config(reader); - assertEquals(80, config.getPort()); - assertEquals("/var/wlgb/git", config.getRootGitDirectory()); - assertEquals("http://127.0.0.1:60000/api/v0/", config.getAPIBaseURL()); - assertEquals("http://127.0.0.1/", config.getPostbackURL()); - assertEquals("Overleaf", config.getServiceName()); - assertTrue(config.isUsingOauth2()); - assertEquals("clientID", config.getOauth2().getOauth2ClientID()); - assertEquals("oauth2 client secret", config.getOauth2().getOauth2ClientSecret()); - assertEquals("https://www.overleaf.com", config.getOauth2().getOauth2Server()); - } + @Test + public void testConstructWithOauth() { + Reader reader = + new StringReader( + "{\n" + + " \"port\": 80,\n" + + " \"bindIp\": \"127.0.0.1\",\n" + + " \"idleTimeout\": 30000,\n" + + " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + + " \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" + + " \"postbackBaseUrl\": \"http://127.0.0.1\",\n" + + " \"serviceName\": \"Overleaf\",\n" + + " \"oauth2\": {\n" + + " \"oauth2ClientID\": \"clientID\",\n" + + " \"oauth2ClientSecret\": \"oauth2 client secret\",\n" + + " \"oauth2Server\": \"https://www.overleaf.com\"\n" + + " }\n" + + "}\n"); + Config config = new Config(reader); + assertEquals(80, config.getPort()); + assertEquals("/var/wlgb/git", config.getRootGitDirectory()); + assertEquals("http://127.0.0.1:60000/api/v0/", config.getAPIBaseURL()); + assertEquals("http://127.0.0.1/", config.getPostbackURL()); + assertEquals("Overleaf", config.getServiceName()); + assertTrue(config.isUsingOauth2()); + assertEquals("clientID", config.getOauth2().getOauth2ClientID()); + assertEquals("oauth2 client secret", config.getOauth2().getOauth2ClientSecret()); + assertEquals("https://www.overleaf.com", config.getOauth2().getOauth2Server()); + } - @Test (expected = AssertionError.class) - public void testConstructWithoutOauth() { - Reader reader = new StringReader("{\n" + - " \"port\": 80,\n" + - " \"bindIp\": \"127.0.0.1\",\n" + - " \"idleTimeout\": 30000,\n" + - " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + - " \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" + - " \"postbackBaseUrl\": \"http://127.0.0.1\",\n" + - " \"serviceName\": \"Overleaf\",\n" + - " \"oauth2\": null\n" + - "}\n"); - Config config = new Config(reader); - assertEquals(80, config.getPort()); - assertEquals("/var/wlgb/git", config.getRootGitDirectory()); - assertEquals("http://127.0.0.1:60000/api/v0/", config.getAPIBaseURL()); - assertEquals("http://127.0.0.1/", config.getPostbackURL()); - assertEquals("Overleaf", config.getServiceName()); - assertFalse(config.isUsingOauth2()); - config.getOauth2(); - } - - @Test - public void asSanitised() throws Exception { - Reader reader = new StringReader("{\n" + - " \"port\": 80,\n" + - " \"bindIp\": \"127.0.0.1\",\n" + - " \"idleTimeout\": 30000,\n" + - " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + - " \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" + - " \"postbackBaseUrl\": \"http://127.0.0.1\",\n" + - " \"serviceName\": \"Overleaf\",\n" + - " \"oauth2\": {\n" + - " \"oauth2ClientID\": \"my oauth2 client id\",\n" + - " \"oauth2ClientSecret\": \"my oauth2 client secret\",\n" + - " \"oauth2Server\": \"https://www.overleaf.com\"\n" + - " }\n" + - "}\n"); - Config config = new Config(reader); - String expected = "{\n" + - " \"port\": 80,\n" + - " \"bindIp\": \"127.0.0.1\",\n" + - " \"idleTimeout\": 30000,\n" + - " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + - " \"apiBaseURL\": \"http://127.0.0.1:60000/api/v0/\",\n" + - " \"postbackURL\": \"http://127.0.0.1/\",\n" + - " \"serviceName\": \"Overleaf\",\n" + - " \"oauth2\": {\n" + - " \"oauth2ClientID\": \"\",\n" + - " \"oauth2ClientSecret\": \"\",\n" + - " \"oauth2Server\": \"https://www.overleaf.com\"\n" + - " },\n" + - " \"userPasswordEnabled\": false,\n" + - " \"repoStore\": null,\n" + - " \"swapStore\": null,\n" + - " \"swapJob\": null,\n" + - " \"sqliteHeapLimitBytes\": 0\n" + - "}"; - assertEquals( - "sanitised config did not hide sensitive fields", - expected, - config.getSanitisedString() - ); - } + @Test(expected = AssertionError.class) + public void testConstructWithoutOauth() { + Reader reader = + new StringReader( + "{\n" + + " \"port\": 80,\n" + + " \"bindIp\": \"127.0.0.1\",\n" + + " \"idleTimeout\": 30000,\n" + + " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + + " \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" + + " \"postbackBaseUrl\": \"http://127.0.0.1\",\n" + + " \"serviceName\": \"Overleaf\",\n" + + " \"oauth2\": null\n" + + "}\n"); + Config config = new Config(reader); + assertEquals(80, config.getPort()); + assertEquals("/var/wlgb/git", config.getRootGitDirectory()); + assertEquals("http://127.0.0.1:60000/api/v0/", config.getAPIBaseURL()); + assertEquals("http://127.0.0.1/", config.getPostbackURL()); + assertEquals("Overleaf", config.getServiceName()); + assertFalse(config.isUsingOauth2()); + config.getOauth2(); + } + @Test + public void asSanitised() throws Exception { + Reader reader = + new StringReader( + "{\n" + + " \"port\": 80,\n" + + " \"bindIp\": \"127.0.0.1\",\n" + + " \"idleTimeout\": 30000,\n" + + " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + + " \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" + + " \"postbackBaseUrl\": \"http://127.0.0.1\",\n" + + " \"serviceName\": \"Overleaf\",\n" + + " \"oauth2\": {\n" + + " \"oauth2ClientID\": \"my oauth2 client id\",\n" + + " \"oauth2ClientSecret\": \"my oauth2 client secret\",\n" + + " \"oauth2Server\": \"https://www.overleaf.com\"\n" + + " }\n" + + "}\n"); + Config config = new Config(reader); + String expected = + "{\n" + + " \"port\": 80,\n" + + " \"bindIp\": \"127.0.0.1\",\n" + + " \"idleTimeout\": 30000,\n" + + " \"rootGitDirectory\": \"/var/wlgb/git\",\n" + + " \"apiBaseURL\": \"http://127.0.0.1:60000/api/v0/\",\n" + + " \"postbackURL\": \"http://127.0.0.1/\",\n" + + " \"serviceName\": \"Overleaf\",\n" + + " \"oauth2\": {\n" + + " \"oauth2ClientID\": \"\",\n" + + " \"oauth2ClientSecret\": \"\",\n" + + " \"oauth2Server\": \"https://www.overleaf.com\"\n" + + " },\n" + + " \"userPasswordEnabled\": false,\n" + + " \"repoStore\": null,\n" + + " \"swapStore\": null,\n" + + " \"swapJob\": null,\n" + + " \"sqliteHeapLimitBytes\": 0\n" + + "}"; + assertEquals( + "sanitised config did not hide sensitive fields", expected, config.getSanitisedString()); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/BridgeTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/BridgeTest.java index 0c6ac74344..48899b3a16 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/BridgeTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/BridgeTest.java @@ -1,5 +1,13 @@ package uk.ac.ic.wlgitbridge.bridge; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import uk.ac.ic.wlgitbridge.application.config.Config; @@ -17,96 +25,66 @@ import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocResult; -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.Optional; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; - -/** +/* * Created by winston on 20/08/2016. */ public class BridgeTest { - private Bridge bridge; + private Bridge bridge; - private ProjectLock lock; - private RepoStore repoStore; - private DBStore dbStore; - private SwapStore swapStore; - private SnapshotApiFacade snapshotAPI; - private ResourceCache resourceCache; - private SwapJob swapJob; - private GcJob gcJob; + private ProjectLock lock; + private RepoStore repoStore; + private DBStore dbStore; + private SwapStore swapStore; + private SnapshotApiFacade snapshotAPI; + private ResourceCache resourceCache; + private SwapJob swapJob; + private GcJob gcJob; - @Before - public void setup() { - lock = mock(ProjectLock.class); - repoStore = mock(RepoStore.class); - dbStore = mock(DBStore.class); - swapStore = mock(SwapStore.class); - snapshotAPI = mock(SnapshotApiFacade.class); - resourceCache = mock(ResourceCache.class); - swapJob = mock(SwapJob.class); - gcJob = mock(GcJob.class); - bridge = new Bridge( - new Config( - 0, - "", - 0, - "", - "", - "", - "", - null, - false, - null, - null, - null, - 0), - lock, - repoStore, - dbStore, - swapStore, - swapJob, - gcJob, - snapshotAPI, - resourceCache - ); - } + @Before + public void setup() { + lock = mock(ProjectLock.class); + repoStore = mock(RepoStore.class); + dbStore = mock(DBStore.class); + swapStore = mock(SwapStore.class); + snapshotAPI = mock(SnapshotApiFacade.class); + resourceCache = mock(ResourceCache.class); + swapJob = mock(SwapJob.class); + gcJob = mock(GcJob.class); + bridge = + new Bridge( + new Config(0, "", 0, "", "", "", "", null, false, null, null, null, 0), + lock, + repoStore, + dbStore, + swapStore, + swapJob, + gcJob, + snapshotAPI, + resourceCache); + } - @Test - public void shutdownStopsSwapAndGcJobs() { - bridge.startBackgroundJobs(); - verify(swapJob).start(); - verify(gcJob).start(); - bridge.doShutdown(); - verify(swapJob).stop(); - verify(gcJob).stop(); - } - - @Test - public void updatingRepositorySetsLastAccessedTime( - ) throws IOException, GitUserException, CannotAcquireLockException { - ProjectRepo repo = mock(ProjectRepo.class); - when(repoStore.getExistingRepo("asdf")).thenReturn(repo); - when(dbStore.getProjectState("asdf")).thenReturn(ProjectState.PRESENT); - when(snapshotAPI.projectExists(Optional.empty(), "asdf")).thenReturn(true); - when( - snapshotAPI.getDoc(Optional.empty(), "asdf") - ).thenReturn(Optional.of(mock(GetDocResult.class))); - when( - snapshotAPI.getSnapshots( - any(), - any(), - anyInt() - ) - ).thenReturn(new ArrayDeque<>()); - bridge.getUpdatedRepo(Optional.empty(), "asdf"); - verify(dbStore).setLastAccessedTime(eq("asdf"), any()); - } + @Test + public void shutdownStopsSwapAndGcJobs() { + bridge.startBackgroundJobs(); + verify(swapJob).start(); + verify(gcJob).start(); + bridge.doShutdown(); + verify(swapJob).stop(); + verify(gcJob).stop(); + } + @Test + public void updatingRepositorySetsLastAccessedTime() + throws IOException, GitUserException, CannotAcquireLockException { + ProjectRepo repo = mock(ProjectRepo.class); + when(repoStore.getExistingRepo("asdf")).thenReturn(repo); + when(dbStore.getProjectState("asdf")).thenReturn(ProjectState.PRESENT); + when(snapshotAPI.projectExists(Optional.empty(), "asdf")).thenReturn(true); + when(snapshotAPI.getDoc(Optional.empty(), "asdf")) + .thenReturn(Optional.of(mock(GetDocResult.class))); + when(snapshotAPI.getSnapshots(any(), any(), anyInt())).thenReturn(new ArrayDeque<>()); + bridge.getUpdatedRepo(Optional.empty(), "asdf"); + verify(dbStore).setLastAccessedTime(eq("asdf"), any()); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStoreTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStoreTest.java index faf30e390b..65fe3ed9e8 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStoreTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/SqliteDBStoreTest.java @@ -1,173 +1,135 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite; -import org.junit.Before; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.io.IOException; import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import uk.ac.ic.wlgitbridge.bridge.db.ProjectState; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -/** +/* * Created by winston on 23/08/2016. */ public class SqliteDBStoreTest { - private SqliteDBStore dbStore; + private SqliteDBStore dbStore; - @Before - public void setup() throws IOException { - TemporaryFolder tmpFolder = new TemporaryFolder(); - tmpFolder.create(); - dbStore = new SqliteDBStore(tmpFolder.newFile("dbStore.db")); - } + @Before + public void setup() throws IOException { + TemporaryFolder tmpFolder = new TemporaryFolder(); + tmpFolder.create(); + dbStore = new SqliteDBStore(tmpFolder.newFile("dbStore.db")); + } - @Test - public void testGetNumProjects() { - assertEquals(0, dbStore.getNumProjects()); - dbStore.setLatestVersionForProject("asdf", 1); - assertEquals(1, dbStore.getNumProjects()); - dbStore.setLatestVersionForProject("asdf1", 2); - assertEquals(2, dbStore.getNumProjects()); - dbStore.setLatestVersionForProject("asdf1", 3); - assertEquals(2, dbStore.getNumProjects()); - } + @Test + public void testGetNumProjects() { + assertEquals(0, dbStore.getNumProjects()); + dbStore.setLatestVersionForProject("asdf", 1); + assertEquals(1, dbStore.getNumProjects()); + dbStore.setLatestVersionForProject("asdf1", 2); + assertEquals(2, dbStore.getNumProjects()); + dbStore.setLatestVersionForProject("asdf1", 3); + assertEquals(2, dbStore.getNumProjects()); + } - @Test - public void swapTableStartsOutEmpty() { - assertNull(dbStore.getOldestUnswappedProject()); - } + @Test + public void swapTableStartsOutEmpty() { + assertNull(dbStore.getOldestUnswappedProject()); + } - @Test - public void testGetOldestUnswappedProject() { - dbStore.setLatestVersionForProject("older", 3); - dbStore.setLastAccessedTime( - "older", - Timestamp.valueOf( - LocalDateTime.now().minus(5, ChronoUnit.SECONDS) - ) - ); - dbStore.setLatestVersionForProject("asdf", 1); - dbStore.setLastAccessedTime( - "asdf", - Timestamp.valueOf( - LocalDateTime.now().minus(1, ChronoUnit.SECONDS) - ) - ); - assertEquals("older", dbStore.getOldestUnswappedProject()); - dbStore.setLastAccessedTime( - "older", - Timestamp.valueOf( - LocalDateTime.now() - ) - ); - assertEquals("asdf", dbStore.getOldestUnswappedProject()); - } + @Test + public void testGetOldestUnswappedProject() { + dbStore.setLatestVersionForProject("older", 3); + dbStore.setLastAccessedTime( + "older", Timestamp.valueOf(LocalDateTime.now().minus(5, ChronoUnit.SECONDS))); + dbStore.setLatestVersionForProject("asdf", 1); + dbStore.setLastAccessedTime( + "asdf", Timestamp.valueOf(LocalDateTime.now().minus(1, ChronoUnit.SECONDS))); + assertEquals("older", dbStore.getOldestUnswappedProject()); + dbStore.setLastAccessedTime("older", Timestamp.valueOf(LocalDateTime.now())); + assertEquals("asdf", dbStore.getOldestUnswappedProject()); + } - @Test - public void swapAndRestore() { - String projectName = "something"; - String compression = "bzip2"; - dbStore.setLatestVersionForProject(projectName, 42); - dbStore.swap(projectName, compression); - assertNull(dbStore.getOldestUnswappedProject()); - assertEquals(dbStore.getSwapCompression(projectName), compression); - // and restore - dbStore.restore(projectName); - assertEquals(dbStore.getSwapCompression(projectName), null); - } + @Test + public void swapAndRestore() { + String projectName = "something"; + String compression = "bzip2"; + dbStore.setLatestVersionForProject(projectName, 42); + dbStore.swap(projectName, compression); + assertNull(dbStore.getOldestUnswappedProject()); + assertEquals(dbStore.getSwapCompression(projectName), compression); + // and restore + dbStore.restore(projectName); + assertEquals(dbStore.getSwapCompression(projectName), null); + } - @Test - public void noOldestProjectIfAllEvicted() { - dbStore.setLatestVersionForProject("older", 3); - dbStore.swap("older", "bzip2"); - assertNull(dbStore.getOldestUnswappedProject()); - } + @Test + public void noOldestProjectIfAllEvicted() { + dbStore.setLatestVersionForProject("older", 3); + dbStore.swap("older", "bzip2"); + assertNull(dbStore.getOldestUnswappedProject()); + } - @Test - public void nullLastAccessedTimesDoNotCount() { - dbStore.setLatestVersionForProject("older", 2); - dbStore.setLastAccessedTime( - "older", - Timestamp.valueOf( - LocalDateTime.now().minus(5, ChronoUnit.SECONDS) - ) - ); - dbStore.setLatestVersionForProject("newer", 3); - dbStore.setLastAccessedTime( - "newer", - Timestamp.valueOf( - LocalDateTime.now() - ) - ); - assertEquals("older", dbStore.getOldestUnswappedProject()); - dbStore.swap("older", "bzip2"); - assertEquals("newer", dbStore.getOldestUnswappedProject()); - } + @Test + public void nullLastAccessedTimesDoNotCount() { + dbStore.setLatestVersionForProject("older", 2); + dbStore.setLastAccessedTime( + "older", Timestamp.valueOf(LocalDateTime.now().minus(5, ChronoUnit.SECONDS))); + dbStore.setLatestVersionForProject("newer", 3); + dbStore.setLastAccessedTime("newer", Timestamp.valueOf(LocalDateTime.now())); + assertEquals("older", dbStore.getOldestUnswappedProject()); + dbStore.swap("older", "bzip2"); + assertEquals("newer", dbStore.getOldestUnswappedProject()); + } - @Test - public void missingProjectLastAccessedTimeCanBeSet() { - dbStore.setLatestVersionForProject("asdf", 1); - dbStore.setLastAccessedTime( - "asdf", - Timestamp.valueOf(LocalDateTime.now()) - ); - assertEquals("asdf", dbStore.getOldestUnswappedProject()); - } + @Test + public void missingProjectLastAccessedTimeCanBeSet() { + dbStore.setLatestVersionForProject("asdf", 1); + dbStore.setLastAccessedTime("asdf", Timestamp.valueOf(LocalDateTime.now())); + assertEquals("asdf", dbStore.getOldestUnswappedProject()); + } - @Test - public void testGetNumUnswappedProjects() { - dbStore.setLatestVersionForProject("asdf", 1); - dbStore.setLastAccessedTime( - "asdf", - Timestamp.valueOf(LocalDateTime.now()) - ); - assertEquals(1, dbStore.getNumUnswappedProjects()); - dbStore.swap( - "asdf", - "bzip2" - ); - assertEquals(0, dbStore.getNumUnswappedProjects()); - } + @Test + public void testGetNumUnswappedProjects() { + dbStore.setLatestVersionForProject("asdf", 1); + dbStore.setLastAccessedTime("asdf", Timestamp.valueOf(LocalDateTime.now())); + assertEquals(1, dbStore.getNumUnswappedProjects()); + dbStore.swap("asdf", "bzip2"); + assertEquals(0, dbStore.getNumUnswappedProjects()); + } - @Test - public void projectStateIsNotPresentIfNotInDBAtAll() { - assertEquals( - ProjectState.NOT_PRESENT, - dbStore.getProjectState("asdf") - ); - } + @Test + public void projectStateIsNotPresentIfNotInDBAtAll() { + assertEquals(ProjectState.NOT_PRESENT, dbStore.getProjectState("asdf")); + } - @Test - public void projectStateIsPresentIfProjectHasLastAccessed() { - dbStore.setLatestVersionForProject("asdf", 1); - dbStore.setLastAccessedTime( - "asdf", - Timestamp.valueOf(LocalDateTime.now()) - ); - assertEquals(ProjectState.PRESENT, dbStore.getProjectState("asdf")); - } + @Test + public void projectStateIsPresentIfProjectHasLastAccessed() { + dbStore.setLatestVersionForProject("asdf", 1); + dbStore.setLastAccessedTime("asdf", Timestamp.valueOf(LocalDateTime.now())); + assertEquals(ProjectState.PRESENT, dbStore.getProjectState("asdf")); + } - @Test - public void projectStateIsSwappedIfLastAccessedIsNull() { - dbStore.setLatestVersionForProject("asdf", 1); - dbStore.swap("asdf", "bzip2"); - assertEquals(ProjectState.SWAPPED, dbStore.getProjectState("asdf")); - } + @Test + public void projectStateIsSwappedIfLastAccessedIsNull() { + dbStore.setLatestVersionForProject("asdf", 1); + dbStore.swap("asdf", "bzip2"); + assertEquals(ProjectState.SWAPPED, dbStore.getProjectState("asdf")); + } - @Test - public void testDeleteProject() { - dbStore.setLatestVersionForProject("project1", 1); - dbStore.setLatestVersionForProject("project2", 1); - assertEquals(ProjectState.PRESENT, dbStore.getProjectState("project1")); - assertEquals(ProjectState.PRESENT, dbStore.getProjectState("project2")); - dbStore.deleteProject("project1"); - assertEquals(ProjectState.NOT_PRESENT, dbStore.getProjectState("project1")); - assertEquals(ProjectState.PRESENT, dbStore.getProjectState("project2")); - } + @Test + public void testDeleteProject() { + dbStore.setLatestVersionForProject("project1", 1); + dbStore.setLatestVersionForProject("project2", 1); + assertEquals(ProjectState.PRESENT, dbStore.getProjectState("project1")); + assertEquals(ProjectState.PRESENT, dbStore.getProjectState("project2")); + dbStore.deleteProject("project1"); + assertEquals(ProjectState.NOT_PRESENT, dbStore.getProjectState("project1")); + assertEquals(ProjectState.PRESENT, dbStore.getProjectState("project2")); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdateTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdateTest.java index 4cf2c21d57..03919bfccd 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdateTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/db/sqlite/update/delete/DeleteFilesForProjectSQLUpdateTest.java @@ -1,24 +1,17 @@ package uk.ac.ic.wlgitbridge.bridge.db.sqlite.update.delete; -import org.junit.Test; import static org.junit.Assert.*; +import org.junit.Test; + public class DeleteFilesForProjectSQLUpdateTest { - @Test - public void testGetSQL() { - DeleteFilesForProjectSQLUpdate update = - new DeleteFilesForProjectSQLUpdate( - "projname", - "path1", - "path2" - ); - assertEquals( - "DELETE FROM `url_index_store` " + - "WHERE `project_name` = ? " + - "AND path IN (?, ?);\n", - update.getSQL() - ); - } - -} \ No newline at end of file + @Test + public void testGetSQL() { + DeleteFilesForProjectSQLUpdate update = + new DeleteFilesForProjectSQLUpdate("projname", "path1", "path2"); + assertEquals( + "DELETE FROM `url_index_store` " + "WHERE `project_name` = ? " + "AND path IN (?, ?);\n", + update.getSQL()); + } +} diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImplTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImplTest.java index 4db48f91d0..fef30e7d23 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImplTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/gc/GcJobImplTest.java @@ -1,5 +1,13 @@ package uk.ac.ic.wlgitbridge.bridge.gc; +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -11,113 +19,101 @@ import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore; import uk.ac.ic.wlgitbridge.data.CannotAcquireLockException; import uk.ac.ic.wlgitbridge.data.ProjectLockImpl; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.*; - -/** +/* * Created by winston on 16/02/2017. */ public class GcJobImplTest { - RepoStore repoStore = mock(RepoStore.class); + RepoStore repoStore = mock(RepoStore.class); - ProjectLock locks; + ProjectLock locks; - GcJobImpl gcJob; + GcJobImpl gcJob; - @Before - public void setup() { - locks = new ProjectLockImpl(); - gcJob = new GcJobImpl(repoStore, locks, 5); + @Before + public void setup() { + locks = new ProjectLockImpl(); + gcJob = new GcJobImpl(repoStore, locks, 5); + } + + @After + public void teardown() { + gcJob.stop(); + } + + @Test + public void addedProjectsAreAllEventuallyGcedOnce() throws Exception { + int numProjects = 5; + /* Make the mocks, make expectations, and keep a reference to them */ + final OngoingStubbing[] o = + new OngoingStubbing[] {when(repoStore.getExistingRepo(anyString()))}; + List mockRepos = + IntStream.range(0, numProjects) + .mapToObj(i -> String.valueOf((char) ('a' + i))) + .map( + proj -> { + gcJob.queueForGc(proj); + ProjectRepo mockRepo = mock(ProjectRepo.class); + o[0] = o[0].thenReturn(mockRepo); + return mockRepo; + }) + .collect(Collectors.toList()); + CompletableFuture fut = gcJob.waitForRun(); + gcJob.start(); + fut.join(); + for (ProjectRepo mock : mockRepos) { + verify(mock).runGC(); + verify(mock).deleteIncomingPacks(); } + /* Nothing should happen on the next run */ + when(repoStore.getExistingRepo(anyString())).thenThrow(new IllegalStateException()); + gcJob.waitForRun().join(); + } - @After - public void teardown() { - gcJob.stop(); - } - - @Test - public void addedProjectsAreAllEventuallyGcedOnce() throws Exception { - int numProjects = 5; - /* Make the mocks, make expectations, and keep a reference to them */ - final OngoingStubbing[] o = new OngoingStubbing[] { - when(repoStore.getExistingRepo(anyString())) - }; - List mockRepos = IntStream.range( - 0, numProjects - ).mapToObj(i -> - String.valueOf((char) ('a' + i)) - ).map(proj -> { - gcJob.queueForGc(proj); - ProjectRepo mockRepo = mock(ProjectRepo.class); - o[0] = o[0].thenReturn(mockRepo); - return mockRepo; - }).collect(Collectors.toList()); - CompletableFuture fut = gcJob.waitForRun(); - gcJob.start(); - fut.join(); - for (ProjectRepo mock : mockRepos) { - verify(mock).runGC(); - verify(mock).deleteIncomingPacks(); - } - /* Nothing should happen on the next run */ - when(repoStore.getExistingRepo(anyString())).thenThrow( - new IllegalStateException() - ); - gcJob.waitForRun().join(); - } - - @Test - public void cannotOverlapGcRuns() throws Exception { - CompletableFuture runningForever = new CompletableFuture<>(); - gcJob.onPostGc(() -> { - try { - /* Pretend the GC is taking forever */ - runningForever.join(); - } catch (Throwable e) { - runningForever.completeExceptionally(e); - } + @Test + public void cannotOverlapGcRuns() throws Exception { + CompletableFuture runningForever = new CompletableFuture<>(); + gcJob.onPostGc( + () -> { + try { + /* Pretend the GC is taking forever */ + runningForever.join(); + } catch (Throwable e) { + runningForever.completeExceptionally(e); + } }); - CompletableFuture fut = gcJob.waitForRun(); - gcJob.start(); - fut.join(); - CompletableFuture ranAgain = new CompletableFuture<>(); - gcJob.onPreGc(() -> ranAgain.complete(null)); - /* Should not run again any time soon */ - for (int i = 0; i < 50; ++i) { - assertFalse(ranAgain.isDone()); - /* The gc interval is 5 ms, so 50 1ms sleeps should be more than - enough without making the test slow */ - Thread.sleep(1); - } - assertFalse(runningForever.isCompletedExceptionally()); + CompletableFuture fut = gcJob.waitForRun(); + gcJob.start(); + fut.join(); + CompletableFuture ranAgain = new CompletableFuture<>(); + gcJob.onPreGc(() -> ranAgain.complete(null)); + /* Should not run again any time soon */ + for (int i = 0; i < 50; ++i) { + assertFalse(ranAgain.isDone()); + /* The gc interval is 5 ms, so 50 1ms sleeps should be more than + enough without making the test slow */ + Thread.sleep(1); } + assertFalse(runningForever.isCompletedExceptionally()); + } - @Test - public void willNotGcProjectUntilItIsUnlocked() - throws InterruptedException, IOException { - ProjectRepo repo = mock(ProjectRepo.class); - when(repoStore.getExistingRepo(anyString())).thenReturn(repo); - gcJob.onPostGc(gcJob::stop); - gcJob.queueForGc("a"); - CompletableFuture fut = gcJob.waitForRun(); - try (LockGuard __ = locks.lockGuard("a")) { - gcJob.start(); - for (int i = 0; i < 50; ++i) { - assertFalse(fut.isDone()); - Thread.sleep(1); - } - } catch (CannotAcquireLockException e) { - throw new RuntimeException(e); - } - /* Now that we've released the lock, fut should complete */ - fut.join(); + @Test + public void willNotGcProjectUntilItIsUnlocked() throws InterruptedException, IOException { + ProjectRepo repo = mock(ProjectRepo.class); + when(repoStore.getExistingRepo(anyString())).thenReturn(repo); + gcJob.onPostGc(gcJob::stop); + gcJob.queueForGc("a"); + CompletableFuture fut = gcJob.waitForRun(); + try (LockGuard __ = locks.lockGuard("a")) { + gcJob.start(); + for (int i = 0; i < 50; ++i) { + assertFalse(fut.isDone()); + Thread.sleep(1); + } + } catch (CannotAcquireLockException e) { + throw new RuntimeException(e); } - + /* Now that we've released the lock, fut should complete */ + fut.join(); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStoreTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStoreTest.java index fae03e50a4..0e4b01ff21 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStoreTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/FSGitRepoStoreTest.java @@ -1,93 +1,80 @@ package uk.ac.ic.wlgitbridge.bridge.repo; -import org.apache.commons.io.FileUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import uk.ac.ic.wlgitbridge.util.Files; +import static org.junit.Assert.*; import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Optional; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import uk.ac.ic.wlgitbridge.util.Files; -import static org.junit.Assert.*; - -/** +/* * Created by winston on 23/08/2016. */ public class FSGitRepoStoreTest { - public static File makeTempRepoDir( - TemporaryFolder tmpFolder, - String name - ) throws IOException { - File tmp = tmpFolder.newFolder(name); - Path rootdir = Paths.get( - "src/test/resources/uk/ac/ic/wlgitbridge/" - + "bridge/repo/FSGitRepoStoreTest/rootdir" - ); - FileUtils.copyDirectory(rootdir.toFile(), tmp); - Files.renameAll(tmp, "DOTgit", ".git"); - return tmp; + public static File makeTempRepoDir(TemporaryFolder tmpFolder, String name) throws IOException { + File tmp = tmpFolder.newFolder(name); + Path rootdir = + Paths.get( + "src/test/resources/uk/ac/ic/wlgitbridge/" + "bridge/repo/FSGitRepoStoreTest/rootdir"); + FileUtils.copyDirectory(rootdir.toFile(), tmp); + Files.renameAll(tmp, "DOTgit", ".git"); + return tmp; + } + + private FSGitRepoStore repoStore; + private File original; + + @Before + public void setup() throws IOException { + TemporaryFolder tmpFolder = new TemporaryFolder(); + tmpFolder.create(); + File tmp = makeTempRepoDir(tmpFolder, "rootdir"); + original = tmpFolder.newFolder("original"); + FileUtils.copyDirectory(tmp, original); + repoStore = new FSGitRepoStore(tmp.getAbsolutePath(), Optional.empty()); + } + + @Test + public void testPurgeNonexistentProjects() { + File toDelete = new File(repoStore.getRootDirectory(), "idontexist"); + File wlgb = new File(repoStore.getRootDirectory(), ".wlgb"); + assertTrue(toDelete.exists()); + assertTrue(wlgb.exists()); + repoStore.purgeNonexistentProjects(Arrays.asList("proj1", "proj2")); + assertFalse(toDelete.exists()); + assertTrue(wlgb.exists()); + } + + @Test + public void totalSizeShouldChangeWhenFilesAreCreatedAndDeleted() throws IOException { + long old = repoStore.totalSize(); + File temp = new File(repoStore.getRootDirectory(), "__temp.txt"); + try (OutputStream out = new FileOutputStream(temp)) { + out.write(new byte[16 * 1024 * 1024]); } + long new_ = repoStore.totalSize(); + assertTrue(new_ > old); + assertTrue(temp.delete()); + long new__ = repoStore.totalSize(); + assertTrue(new__ < new_); + } - private FSGitRepoStore repoStore; - private File original; - - @Before - public void setup() throws IOException { - TemporaryFolder tmpFolder = new TemporaryFolder(); - tmpFolder.create(); - File tmp = makeTempRepoDir(tmpFolder, "rootdir"); - original = tmpFolder.newFolder("original"); - FileUtils.copyDirectory(tmp, original); - repoStore = new FSGitRepoStore(tmp.getAbsolutePath(), Optional.empty()); - } - - @Test - public void testPurgeNonexistentProjects() { - File toDelete = new File( - repoStore.getRootDirectory(), "idontexist" - ); - File wlgb = new File(repoStore.getRootDirectory(), ".wlgb"); - assertTrue(toDelete.exists()); - assertTrue(wlgb.exists()); - repoStore.purgeNonexistentProjects(Arrays.asList("proj1", "proj2")); - assertFalse(toDelete.exists()); - assertTrue(wlgb.exists()); - } - - @Test - public void totalSizeShouldChangeWhenFilesAreCreatedAndDeleted() - throws IOException { - long old = repoStore.totalSize(); - File temp = new File(repoStore.getRootDirectory(), "__temp.txt"); - try ( - OutputStream out = new FileOutputStream( - temp - ) - ) { - out.write(new byte[16 * 1024 * 1024]); - } - long new_ = repoStore.totalSize(); - assertTrue(new_ > old); - assertTrue(temp.delete()); - long new__ = repoStore.totalSize(); - assertTrue(new__ < new_); - } - - @Test - public void zipAndUnzipShouldBeTheSame() throws IOException { - File expected = new File(original, "proj1"); - File actual = new File(repoStore.getRootDirectory(), "proj1"); - assertTrue(Files.contentsAreEqual(expected, actual)); - InputStream zipped = repoStore.bzip2Project("proj1"); - repoStore.remove("proj1"); - assertFalse(actual.exists()); - repoStore.unbzip2Project("proj1", zipped); - assertTrue(Files.contentsAreEqual(expected, actual)); - } - -} \ No newline at end of file + @Test + public void zipAndUnzipShouldBeTheSame() throws IOException { + File expected = new File(original, "proj1"); + File actual = new File(repoStore.getRootDirectory(), "proj1"); + assertTrue(Files.contentsAreEqual(expected, actual)); + InputStream zipped = repoStore.bzip2Project("proj1"); + repoStore.remove("proj1"); + assertFalse(actual.exists()); + repoStore.unbzip2Project("proj1", zipped); + assertTrue(Files.contentsAreEqual(expected, actual)); + } +} diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java index 3f3504b784..7222203223 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/repo/GitProjectRepoTest.java @@ -1,6 +1,17 @@ package uk.ac.ic.wlgitbridge.bridge.repo; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.*; + import com.google.api.client.repackaged.com.google.common.base.Preconditions; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.function.Supplier; import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.Rule; @@ -12,189 +23,150 @@ import uk.ac.ic.wlgitbridge.data.filestore.RepositoryFile; import uk.ac.ic.wlgitbridge.snapshot.servermock.util.FileUtil; import uk.ac.ic.wlgitbridge.util.Files; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; -import java.util.function.Supplier; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.lessThan; -import static org.junit.Assert.*; - -/** +/* * Created by winston on 08/10/2016. */ public class GitProjectRepoTest { - public static File makeTempRepoDir( - TemporaryFolder tmpFolder, - String name - ) throws IOException { - File tmp = tmpFolder.newFolder(name); - Path rootdir = Paths.get( - "src/test/resources/uk/ac/ic/wlgitbridge/" - + "bridge/repo/GitProjectRepoTest/rootdir" - ); - FileUtils.copyDirectory(rootdir.toFile(), tmp); - Files.renameAll(tmp, "DOTgit", ".git"); - return tmp; + public static File makeTempRepoDir(TemporaryFolder tmpFolder, String name) throws IOException { + File tmp = tmpFolder.newFolder(name); + Path rootdir = + Paths.get( + "src/test/resources/uk/ac/ic/wlgitbridge/" + "bridge/repo/GitProjectRepoTest/rootdir"); + FileUtils.copyDirectory(rootdir.toFile(), tmp); + Files.renameAll(tmp, "DOTgit", ".git"); + return tmp; + } + + private File rootdir; + FSGitRepoStore repoStore; + GitProjectRepo repo; + GitProjectRepo badGitignore; + GitProjectRepo incoming; + GitProjectRepo withoutIncoming; + + @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void setup() throws IOException { + rootdir = makeTempRepoDir(tmpFolder, "rootdir"); + repoStore = new FSGitRepoStore(rootdir.getAbsolutePath(), Optional.empty()); + repo = fromExistingDir("repo"); + badGitignore = fromExistingDir("badgitignore"); + incoming = fromExistingDir("incoming"); + withoutIncoming = fromExistingDir("without_incoming"); + } + + private GitProjectRepo fromExistingDir(String dir) throws IOException { + GitProjectRepo ret = GitProjectRepo.fromName(dir); + ret.useExistingRepository(repoStore); + return ret; + } + + private GitDirectoryContents makeDirContents(String... contents) { + Preconditions.checkArgument(contents.length % 2 == 0); + List files = new ArrayList<>(contents.length / 2); + for (int i = 0; i + 1 < contents.length; i += 2) { + files.add(new RepositoryFile(contents[i], contents[i + 1].getBytes(StandardCharsets.UTF_8))); } + return new GitDirectoryContents( + files, + repoStore.getRootDirectory(), + "repo", + "Winston Li", + "git@winston.li", + "Commit Message", + new Date()); + } - private File rootdir; - FSGitRepoStore repoStore; - GitProjectRepo repo; - GitProjectRepo badGitignore; - GitProjectRepo incoming; - GitProjectRepo withoutIncoming; + @Test + public void deletingIgnoredFileOnAppDeletesFromTheRepo() throws IOException { + GitDirectoryContents contents = makeDirContents(".gitignore", "*.ignored\n"); + repo.commitAndGetMissing(contents); + repo.resetHard(); + File dir = repo.getDotGitDir(); + assertEquals( + new HashSet(Arrays.asList(".git", ".gitignore")), + new HashSet(Arrays.asList(dir.list()))); + } - @Rule - public TemporaryFolder tmpFolder = new TemporaryFolder(); + @Test + public void addingIgnoredFilesOnAppAddsToTheRepo() throws IOException { + GitDirectoryContents contents = + makeDirContents( + ".gitignore", + "*.ignored\n", + "file1.ignored", + "", + "file1.txt", + "", + "file2.txt", + "", + "added.ignored", + ""); + repo.commitAndGetMissing(contents); + repo.resetHard(); + assertEquals( + new HashSet( + Arrays.asList( + ".git", ".gitignore", "file1.ignored", "file1.txt", "file2.txt", "added.ignored")), + new HashSet(Arrays.asList(repo.getDotGitDir().list()))); + } - @Before - public void setup() throws IOException { - rootdir = makeTempRepoDir(tmpFolder, "rootdir"); - repoStore = new FSGitRepoStore(rootdir.getAbsolutePath(), Optional.empty()); - repo = fromExistingDir("repo"); - badGitignore = fromExistingDir("badgitignore"); - incoming = fromExistingDir("incoming"); - withoutIncoming = fromExistingDir("without_incoming"); - } + @Test + public void badGitignoreShouldNotThrow() throws IOException { + GitDirectoryContents contents = + makeDirContents( + ".gitignore", + "*.ignored\n", + "file1.ignored", + "", + "file1.txt", + "", + "file2.txt", + "", + "added.ignored", + ""); + badGitignore.commitAndGetMissing(contents); + } - private GitProjectRepo fromExistingDir(String dir) throws IOException { - GitProjectRepo ret = GitProjectRepo.fromName(dir); - ret.useExistingRepository(repoStore); - return ret; - } + private static long repoSize(ProjectRepo repo) { + return FileUtils.sizeOfDirectory(repo.getProjectDir()); + } - private GitDirectoryContents makeDirContents( - String... contents - ) { - Preconditions.checkArgument(contents.length % 2 == 0); - List files = new ArrayList<>(contents.length / 2); - for (int i = 0; i + 1 < contents.length; i += 2) { - files.add( - new RepositoryFile( - contents[i], - contents[i + 1].getBytes(StandardCharsets.UTF_8) - ) - ); - } - return new GitDirectoryContents( - files, - repoStore.getRootDirectory(), - "repo", - "Winston Li", - "git@winston.li", - "Commit Message", - new Date() - ); - } + @Test + public void runGCReducesTheSizeOfARepoWithGarbage() throws IOException { + long beforeSize = repoSize(repo); + repo.runGC(); + long afterSize = repoSize(repo); + assertThat(beforeSize, lessThan(afterSize)); + } - @Test - public void deletingIgnoredFileOnAppDeletesFromTheRepo( - ) throws IOException { - GitDirectoryContents contents = makeDirContents( - ".gitignore", - "*.ignored\n" - ); - repo.commitAndGetMissing(contents); - repo.resetHard(); - File dir = repo.getDotGitDir(); - assertEquals( - new HashSet(Arrays.asList(".git", ".gitignore")), - new HashSet(Arrays.asList(dir.list())) - ); - } + @Test + public void runGCDoesNothingOnARepoWithoutGarbage() throws IOException { + repo.runGC(); + long beforeSize = repoSize(repo); + repo.runGC(); + long afterSize = repoSize(repo); + assertThat(beforeSize, equalTo(afterSize)); + } - @Test - public void addingIgnoredFilesOnAppAddsToTheRepo() throws IOException { - GitDirectoryContents contents = makeDirContents( - ".gitignore", - "*.ignored\n", - "file1.ignored", - "", - "file1.txt", - "", - "file2.txt", - "", - "added.ignored", - "" - ); - repo.commitAndGetMissing(contents); - repo.resetHard(); - assertEquals( - new HashSet(Arrays.asList( - ".git", - ".gitignore", - "file1.ignored", - "file1.txt", - "file2.txt", - "added.ignored" - )), - new HashSet(Arrays.asList(repo.getDotGitDir().list())) - ); - } - - @Test - public void badGitignoreShouldNotThrow() throws IOException { - GitDirectoryContents contents = makeDirContents( - ".gitignore", - "*.ignored\n", - "file1.ignored", - "", - "file1.txt", - "", - "file2.txt", - "", - "added.ignored", - "" - ); - badGitignore.commitAndGetMissing(contents); - } - - private static long repoSize(ProjectRepo repo) { - return FileUtils.sizeOfDirectory(repo.getProjectDir()); - } - - @Test - public void runGCReducesTheSizeOfARepoWithGarbage() throws IOException { - long beforeSize = repoSize(repo); - repo.runGC(); - long afterSize = repoSize(repo); - assertThat(beforeSize, lessThan(afterSize)); - - } - - @Test - public void runGCDoesNothingOnARepoWithoutGarbage() throws IOException { - repo.runGC(); - long beforeSize = repoSize(repo); - repo.runGC(); - long afterSize = repoSize(repo); - assertThat(beforeSize, equalTo(afterSize)); - } - - @Test - public void deleteIncomingPacksDeletesIncomingPacks() throws IOException { - Supplier dirsAreEq = () -> FileUtil.directoryDeepEquals( - incoming.getProjectDir(), withoutIncoming.getProjectDir() - ); - assertFalse(dirsAreEq.get()); - incoming.deleteIncomingPacks(); - assertTrue(dirsAreEq.get()); - } - - @Test - public void deleteIncomingPacksOnDirWithoutIncomingPacksDoesNothing() - throws IOException { - File actual = withoutIncoming.getProjectDir(); - File expected = tmpFolder.newFolder(); - FileUtils.copyDirectory(actual, expected); - withoutIncoming.deleteIncomingPacks(); - assertTrue(FileUtil.directoryDeepEquals(actual, expected)); - } + @Test + public void deleteIncomingPacksDeletesIncomingPacks() throws IOException { + Supplier dirsAreEq = + () -> + FileUtil.directoryDeepEquals(incoming.getProjectDir(), withoutIncoming.getProjectDir()); + assertFalse(dirsAreEq.get()); + incoming.deleteIncomingPacks(); + assertTrue(dirsAreEq.get()); + } + @Test + public void deleteIncomingPacksOnDirWithoutIncomingPacksDoesNothing() throws IOException { + File actual = withoutIncoming.getProjectDir(); + File expected = tmpFolder.newFolder(); + FileUtils.copyDirectory(actual, expected); + withoutIncoming.deleteIncomingPacks(); + assertTrue(FileUtil.directoryDeepEquals(actual, expected)); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCacheTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCacheTest.java index 1c814770cd..862662696c 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCacheTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/resource/UrlResourceCacheTest.java @@ -1,7 +1,16 @@ package uk.ac.ic.wlgitbridge.bridge.resource; -import io.netty.handler.codec.http.HttpHeaders; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import java.io.IOException; +import java.util.HashMap; +import java.util.Optional; +import java.util.concurrent.ExecutionException; import org.junit.Test; import uk.ac.ic.wlgitbridge.bridge.db.DBStore; import uk.ac.ic.wlgitbridge.bridge.util.CastUtil; @@ -9,115 +18,101 @@ import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException; import uk.ac.ic.wlgitbridge.io.http.ning.NingHttpClientFacade; import uk.ac.ic.wlgitbridge.util.FunctionT; -import java.io.IOException; -import java.util.HashMap; -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; - public class UrlResourceCacheTest { - private static String PROJ = "proj"; + private static String PROJ = "proj"; - private static String URL = "http://localhost/file.jpg"; + private static String URL = "http://localhost/file.jpg"; - private static String NEW_PATH = "file1.jpg"; + private static String NEW_PATH = "file1.jpg"; - private final NingHttpClientFacade http = mock(NingHttpClientFacade.class); + private final NingHttpClientFacade http = mock(NingHttpClientFacade.class); - private final DBStore dbStore = mock(DBStore.class); + private final DBStore dbStore = mock(DBStore.class); - private final UrlResourceCache cache = new UrlResourceCache(dbStore, http); + private final UrlResourceCache cache = new UrlResourceCache(dbStore, http); - private static HttpHeaders withContentLength(long cl) { - return new DefaultHttpHeaders().add("Content-Length", String.valueOf(cl)); - } + private static HttpHeaders withContentLength(long cl) { + return new DefaultHttpHeaders().add("Content-Length", String.valueOf(cl)); + } - private void respondWithContentLength(long cl, long actual) - throws ExecutionException { - when(http.get(any(), any())).thenAnswer(invoc -> { - Object[] args = invoc.getArguments(); - //noinspection unchecked - ((FunctionT< - HttpHeaders, Boolean, SizeLimitExceededException - >) args[1]).apply(withContentLength(cl)); - return new byte[CastUtil.assumeInt(actual)]; - }); - } + private void respondWithContentLength(long cl, long actual) throws ExecutionException { + when(http.get(any(), any())) + .thenAnswer( + invoc -> { + Object[] args = invoc.getArguments(); + //noinspection unchecked + ((FunctionT) args[1]) + .apply(withContentLength(cl)); + return new byte[CastUtil.assumeInt(actual)]; + }); + } - private void respondWithContentLength(long cl) throws ExecutionException { - respondWithContentLength(cl, cl); - } + private void respondWithContentLength(long cl) throws ExecutionException { + respondWithContentLength(cl, cl); + } - private void getWithMaxLength(Optional max) - throws IOException, SizeLimitExceededException { - cache.get( - PROJ, URL, NEW_PATH, new HashMap<>(), new HashMap<>(), max); - } + private void getWithMaxLength(Optional max) throws IOException, SizeLimitExceededException { + cache.get(PROJ, URL, NEW_PATH, new HashMap<>(), new HashMap<>(), max); + } - private void getUrl(String url) throws IOException, SizeLimitExceededException { - cache.get(PROJ, url, NEW_PATH, new HashMap<>(), new HashMap<>(), Optional.empty()); - } + private void getUrl(String url) throws IOException, SizeLimitExceededException { + cache.get(PROJ, url, NEW_PATH, new HashMap<>(), new HashMap<>(), Optional.empty()); + } - private void getWithMaxLength(long max) - throws IOException, SizeLimitExceededException { - getWithMaxLength(Optional.of(max)); - } + private void getWithMaxLength(long max) throws IOException, SizeLimitExceededException { + getWithMaxLength(Optional.of(max)); + } - private void getWithoutLimit() - throws IOException, SizeLimitExceededException { - getWithMaxLength(Optional.empty()); - } + private void getWithoutLimit() throws IOException, SizeLimitExceededException { + getWithMaxLength(Optional.empty()); + } - @Test - public void getDoesNotThrowWhenContentLengthLT() throws Exception { - respondWithContentLength(1); - getWithMaxLength(2); - } + @Test + public void getDoesNotThrowWhenContentLengthLT() throws Exception { + respondWithContentLength(1); + getWithMaxLength(2); + } - @Test - public void getDoesNotThrowWhenContentLengthEQ() throws Exception { - respondWithContentLength(2); - getWithMaxLength(2); - } + @Test + public void getDoesNotThrowWhenContentLengthEQ() throws Exception { + respondWithContentLength(2); + getWithMaxLength(2); + } - @Test (expected = SizeLimitExceededException.class) - public void getThrowsSizeLimitExceededWhenContentLengthGT() - throws Exception { - respondWithContentLength(3); - getWithMaxLength(2); - } + @Test(expected = SizeLimitExceededException.class) + public void getThrowsSizeLimitExceededWhenContentLengthGT() throws Exception { + respondWithContentLength(3); + getWithMaxLength(2); + } - @Test - public void getWithEmptyContentIsValid() throws Exception { - respondWithContentLength(0); - getWithMaxLength(0); - } + @Test + public void getWithEmptyContentIsValid() throws Exception { + respondWithContentLength(0); + getWithMaxLength(0); + } - @Test - public void getWithoutLimitDoesNotThrow() throws Exception { - respondWithContentLength(Integer.MAX_VALUE, 0); - getWithoutLimit(); - } + @Test + public void getWithoutLimitDoesNotThrow() throws Exception { + respondWithContentLength(Integer.MAX_VALUE, 0); + getWithoutLimit(); + } - @Test (expected = SizeLimitExceededException.class) - public void getThrowsIfActualContentTooBig() throws Exception { - respondWithContentLength(0, 10); - getWithMaxLength(5); - } - - @Test - public void tokenIsRemovedFromCacheKey() throws Exception { - String url = "http://history.overleaf.com/projects/1234/blobs/abdef?token=secretencryptedstuff&_path=test.tex"; - String cacheKey = "http://history.overleaf.com/projects/1234/blobs/abdef?token=REMOVED&_path=test.tex"; - respondWithContentLength(123); - getUrl(url); - verify(dbStore).getPathForURLInProject(PROJ, cacheKey); - verify(dbStore).addURLIndexForProject(PROJ, cacheKey, NEW_PATH); - } + @Test(expected = SizeLimitExceededException.class) + public void getThrowsIfActualContentTooBig() throws Exception { + respondWithContentLength(0, 10); + getWithMaxLength(5); + } + @Test + public void tokenIsRemovedFromCacheKey() throws Exception { + String url = + "http://history.overleaf.com/projects/1234/blobs/abdef?token=secretencryptedstuff&_path=test.tex"; + String cacheKey = + "http://history.overleaf.com/projects/1234/blobs/abdef?token=REMOVED&_path=test.tex"; + respondWithContentLength(123); + getUrl(url); + verify(dbStore).getPathForURLInProject(PROJ, cacheKey); + verify(dbStore).addURLIndexForProject(PROJ, cacheKey, NEW_PATH); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImplTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImplTest.java index 3e9aa2e491..3956d28dc9 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImplTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/job/SwapJobImplTest.java @@ -1,5 +1,13 @@ package uk.ac.ic.wlgitbridge.bridge.swap.job; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; @@ -15,152 +23,148 @@ import uk.ac.ic.wlgitbridge.bridge.swap.store.InMemorySwapStore; import uk.ac.ic.wlgitbridge.bridge.swap.store.SwapStore; import uk.ac.ic.wlgitbridge.data.ProjectLockImpl; -import java.io.IOException; -import java.sql.Timestamp; -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** +/* * Created by winston on 20/08/2016. */ public class SwapJobImplTest { - private SwapJobImpl swapJob; + private SwapJobImpl swapJob; - private ProjectLock lock; - private RepoStore repoStore; - private DBStore dbStore; - private SwapStore swapStore; + private ProjectLock lock; + private RepoStore repoStore; + private DBStore dbStore; + private SwapStore swapStore; - @Before - public void setup() throws IOException { - TemporaryFolder tmpFolder = new TemporaryFolder(); - tmpFolder.create(); - lock = new ProjectLockImpl(); - repoStore = new FSGitRepoStore( - FSGitRepoStoreTest.makeTempRepoDir( - tmpFolder, - "repostore" - ).getAbsolutePath(), - 100_000, - FileUtils::sizeOfDirectory - ); - dbStore = new SqliteDBStore(tmpFolder.newFile()); - dbStore.setLatestVersionForProject("proj1", 0); - dbStore.setLatestVersionForProject("proj2", 0); - dbStore.setLastAccessedTime( - "proj1", - Timestamp.valueOf(LocalDateTime.now()) - ); - dbStore.setLastAccessedTime( - "proj2", - Timestamp.valueOf( - LocalDateTime.now().minus(1, ChronoUnit.SECONDS) - ) - ); - swapStore = new InMemorySwapStore(); - swapJob = new SwapJobImpl( - 1, - 15000, - 30000, - Duration.ofMillis(100), - SwapJob.CompressionMethod.Bzip2, - lock, - repoStore, - dbStore, - swapStore - ); + @Before + public void setup() throws IOException { + TemporaryFolder tmpFolder = new TemporaryFolder(); + tmpFolder.create(); + lock = new ProjectLockImpl(); + repoStore = + new FSGitRepoStore( + FSGitRepoStoreTest.makeTempRepoDir(tmpFolder, "repostore").getAbsolutePath(), + 100_000, + FileUtils::sizeOfDirectory); + dbStore = new SqliteDBStore(tmpFolder.newFile()); + dbStore.setLatestVersionForProject("proj1", 0); + dbStore.setLatestVersionForProject("proj2", 0); + dbStore.setLastAccessedTime("proj1", Timestamp.valueOf(LocalDateTime.now())); + dbStore.setLastAccessedTime( + "proj2", Timestamp.valueOf(LocalDateTime.now().minus(1, ChronoUnit.SECONDS))); + swapStore = new InMemorySwapStore(); + swapJob = + new SwapJobImpl( + 1, + 15000, + 30000, + Duration.ofMillis(100), + SwapJob.CompressionMethod.Bzip2, + lock, + repoStore, + dbStore, + swapStore); + } + + @After + public void teardown() { + if (swapJob != null) { + swapJob.stop(); } + } - @After - public void teardown() { - if(swapJob != null) { - swapJob.stop(); - } + private void waitASecond() { + try { + Thread.sleep(1 * 1000); + } catch (Exception _e) { } + } - private void waitASecond() { - try { Thread.sleep(1 * 1000); } catch (Exception _e) {} - } + @Test + public void startingTimerAlwaysCausesASwap() { + swapJob.lowWatermarkBytes = 16384; + swapJob.interval = Duration.ofHours(1); + assertEquals(0, swapJob.swaps.get()); + swapJob.start(); + do { + waitASecond(); + } while (swapJob.swaps.get() <= 0); + assertTrue(swapJob.swaps.get() > 0); + } - @Test - public void startingTimerAlwaysCausesASwap() { - swapJob.lowWatermarkBytes = 16384; - swapJob.interval = Duration.ofHours(1); - assertEquals(0, swapJob.swaps.get()); - swapJob.start(); - do { waitASecond(); } while (swapJob.swaps.get() <= 0); - assertTrue(swapJob.swaps.get() > 0); - } + @Test + public void swapsHappenEveryInterval() { + swapJob.lowWatermarkBytes = 16384; + assertEquals(0, swapJob.swaps.get()); + swapJob.start(); + do { + waitASecond(); + } while (swapJob.swaps.get() <= 1); + assertTrue(swapJob.swaps.get() > 1); + } - @Test - public void swapsHappenEveryInterval() { - swapJob.lowWatermarkBytes = 16384; - assertEquals(0, swapJob.swaps.get()); - swapJob.start(); - do { waitASecond(); } while (swapJob.swaps.get() <= 1); - assertTrue(swapJob.swaps.get() > 1); - } + @Test + public void noProjectsGetSwappedWhenUnderHighWatermark() { + swapJob.highWatermarkBytes = 65536; + assertEquals(2, dbStore.getNumUnswappedProjects()); + swapJob.start(); + do { + waitASecond(); + } while (swapJob.swaps.get() < 1); + assertEquals(2, dbStore.getNumUnswappedProjects()); + } - @Test - public void noProjectsGetSwappedWhenUnderHighWatermark() { - swapJob.highWatermarkBytes = 65536; - assertEquals(2, dbStore.getNumUnswappedProjects()); - swapJob.start(); - do { waitASecond(); } while (swapJob.swaps.get() < 1); - assertEquals(2, dbStore.getNumUnswappedProjects()); - } - - @Test - public void correctProjGetSwappedWhenOverHighWatermark( - ) throws IOException { - swapJob.lowWatermarkBytes = 16384; - assertEquals(2, dbStore.getNumUnswappedProjects()); - assertEquals("proj2", dbStore.getOldestUnswappedProject()); - swapJob.start(); - do { waitASecond(); } while (swapJob.swaps.get() < 1); - assertEquals(1, dbStore.getNumUnswappedProjects()); - assertEquals("proj1", dbStore.getOldestUnswappedProject()); - assertEquals("bzip2", dbStore.getSwapCompression("proj2")); - swapJob.restore("proj2"); - assertEquals(null, dbStore.getSwapCompression("proj2")); - int numSwaps = swapJob.swaps.get(); - do { waitASecond(); } while (swapJob.swaps.get() <= numSwaps); - assertEquals(1, dbStore.getNumUnswappedProjects()); - assertEquals("proj2", dbStore.getOldestUnswappedProject()); - } - - @Test - public void swapCompressionGzip() throws IOException { - swapJob = new SwapJobImpl( - 1, - 15000, - 30000, - Duration.ofMillis(100), - SwapJob.CompressionMethod.Gzip, - lock, - repoStore, - dbStore, - swapStore - ); - swapJob.lowWatermarkBytes = 16384; - assertEquals(2, dbStore.getNumUnswappedProjects()); - assertEquals("proj2", dbStore.getOldestUnswappedProject()); - swapJob.start(); - do { waitASecond(); } while (swapJob.swaps.get() < 1); - assertEquals(1, dbStore.getNumUnswappedProjects()); - assertEquals("proj1", dbStore.getOldestUnswappedProject()); - assertEquals("gzip", dbStore.getSwapCompression("proj2")); - swapJob.restore("proj2"); - assertEquals(null, dbStore.getSwapCompression("proj2")); - int numSwaps = swapJob.swaps.get(); - do { waitASecond(); } while (swapJob.swaps.get() <= numSwaps); - assertEquals(1, dbStore.getNumUnswappedProjects()); - assertEquals("proj2", dbStore.getOldestUnswappedProject()); - } + @Test + public void correctProjGetSwappedWhenOverHighWatermark() throws IOException { + swapJob.lowWatermarkBytes = 16384; + assertEquals(2, dbStore.getNumUnswappedProjects()); + assertEquals("proj2", dbStore.getOldestUnswappedProject()); + swapJob.start(); + do { + waitASecond(); + } while (swapJob.swaps.get() < 1); + assertEquals(1, dbStore.getNumUnswappedProjects()); + assertEquals("proj1", dbStore.getOldestUnswappedProject()); + assertEquals("bzip2", dbStore.getSwapCompression("proj2")); + swapJob.restore("proj2"); + assertEquals(null, dbStore.getSwapCompression("proj2")); + int numSwaps = swapJob.swaps.get(); + do { + waitASecond(); + } while (swapJob.swaps.get() <= numSwaps); + assertEquals(1, dbStore.getNumUnswappedProjects()); + assertEquals("proj2", dbStore.getOldestUnswappedProject()); + } + @Test + public void swapCompressionGzip() throws IOException { + swapJob = + new SwapJobImpl( + 1, + 15000, + 30000, + Duration.ofMillis(100), + SwapJob.CompressionMethod.Gzip, + lock, + repoStore, + dbStore, + swapStore); + swapJob.lowWatermarkBytes = 16384; + assertEquals(2, dbStore.getNumUnswappedProjects()); + assertEquals("proj2", dbStore.getOldestUnswappedProject()); + swapJob.start(); + do { + waitASecond(); + } while (swapJob.swaps.get() < 1); + assertEquals(1, dbStore.getNumUnswappedProjects()); + assertEquals("proj1", dbStore.getOldestUnswappedProject()); + assertEquals("gzip", dbStore.getSwapCompression("proj2")); + swapJob.restore("proj2"); + assertEquals(null, dbStore.getSwapCompression("proj2")); + int numSwaps = swapJob.swaps.get(); + do { + waitASecond(); + } while (swapJob.swaps.get() <= numSwaps); + assertEquals(1, dbStore.getNumUnswappedProjects()); + assertEquals("proj2", dbStore.getOldestUnswappedProject()); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStoreTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStoreTest.java index 7e257f2305..a5a9f39e7d 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStoreTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/InMemorySwapStoreTest.java @@ -1,96 +1,56 @@ package uk.ac.ic.wlgitbridge.bridge.swap.store; +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayInputStream; +import java.io.IOException; import org.apache.commons.io.IOUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import uk.ac.ic.wlgitbridge.bridge.swap.store.InMemorySwapStore; -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import static org.junit.Assert.assertArrayEquals; - -/** +/* * Created by winston on 23/08/2016. */ public class InMemorySwapStoreTest { - private final InMemorySwapStore swapStore = new InMemorySwapStore(); + private final InMemorySwapStore swapStore = new InMemorySwapStore(); - @Rule - public final ExpectedException exception = ExpectedException.none(); + @Rule public final ExpectedException exception = ExpectedException.none(); - @Test - public void downloadingNonExistentFileThrows() { - exception.expect(IllegalArgumentException.class); - swapStore.openDownloadStream("asdf"); - } + @Test + public void downloadingNonExistentFileThrows() { + exception.expect(IllegalArgumentException.class); + swapStore.openDownloadStream("asdf"); + } - @Test - public void canDownloadUploadedFiles() throws IOException { - byte[] proj1Contents = "helloproj1".getBytes(); - byte[] proj2Contents = "asdfproj2".getBytes(); - swapStore.upload( - "proj1", - new ByteArrayInputStream(proj1Contents), - proj1Contents.length - ); - swapStore.upload( - "proj2", - new ByteArrayInputStream(proj2Contents), - proj2Contents.length - ); - assertArrayEquals( - proj1Contents, - IOUtils.toByteArray(swapStore.openDownloadStream("proj1")) - ); - assertArrayEquals( - proj2Contents, - IOUtils.toByteArray(swapStore.openDownloadStream("proj2")) - ); - } + @Test + public void canDownloadUploadedFiles() throws IOException { + byte[] proj1Contents = "helloproj1".getBytes(); + byte[] proj2Contents = "asdfproj2".getBytes(); + swapStore.upload("proj1", new ByteArrayInputStream(proj1Contents), proj1Contents.length); + swapStore.upload("proj2", new ByteArrayInputStream(proj2Contents), proj2Contents.length); + assertArrayEquals(proj1Contents, IOUtils.toByteArray(swapStore.openDownloadStream("proj1"))); + assertArrayEquals(proj2Contents, IOUtils.toByteArray(swapStore.openDownloadStream("proj2"))); + } - @Test - public void uploadingForTheSameProjectOverwritesTheFile( - ) throws IOException { - byte[] proj1Contents = "helloproj1".getBytes(); - byte[] proj1NewContents = "goodbyeproj1".getBytes(); - swapStore.upload( - "proj1", - new ByteArrayInputStream(proj1Contents), - proj1Contents.length - ); - assertArrayEquals( - proj1Contents, - IOUtils.toByteArray(swapStore.openDownloadStream("proj1")) - ); - swapStore.upload( - "proj1", - new ByteArrayInputStream(proj1NewContents), - proj1NewContents.length - ); - assertArrayEquals( - proj1NewContents, - IOUtils.toByteArray(swapStore.openDownloadStream("proj1")) - ); - } + @Test + public void uploadingForTheSameProjectOverwritesTheFile() throws IOException { + byte[] proj1Contents = "helloproj1".getBytes(); + byte[] proj1NewContents = "goodbyeproj1".getBytes(); + swapStore.upload("proj1", new ByteArrayInputStream(proj1Contents), proj1Contents.length); + assertArrayEquals(proj1Contents, IOUtils.toByteArray(swapStore.openDownloadStream("proj1"))); + swapStore.upload("proj1", new ByteArrayInputStream(proj1NewContents), proj1NewContents.length); + assertArrayEquals(proj1NewContents, IOUtils.toByteArray(swapStore.openDownloadStream("proj1"))); + } - @Test - public void canRemoveFiles() throws IOException { - byte[] projContents = "total garbage".getBytes(); - swapStore.upload( - "proj", - new ByteArrayInputStream(projContents), - projContents.length - ); - assertArrayEquals( - projContents, - IOUtils.toByteArray(swapStore.openDownloadStream("proj")) - ); - swapStore.remove("proj"); - exception.expect(IllegalArgumentException.class); - swapStore.openDownloadStream("proj"); - } - -} \ No newline at end of file + @Test + public void canRemoveFiles() throws IOException { + byte[] projContents = "total garbage".getBytes(); + swapStore.upload("proj", new ByteArrayInputStream(projContents), projContents.length); + assertArrayEquals(projContents, IOUtils.toByteArray(swapStore.openDownloadStream("proj"))); + swapStore.remove("proj"); + exception.expect(IllegalArgumentException.class); + swapStore.openDownloadStream("proj"); + } +} diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStoreTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStoreTest.java index e417025767..3bbdfe004a 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStoreTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/bridge/swap/store/S3SwapStoreTest.java @@ -2,41 +2,41 @@ package uk.ac.ic.wlgitbridge.bridge.swap.store; import org.junit.Before; -/** +/* * Created by winston on 21/08/2016. */ public class S3SwapStoreTest { - private static final String accessKey = null; - private static final String secret = null; - private static final String bucketName = "com.overleaf.testbucket"; - private static final String region = "us-east-1"; + private static final String accessKey = null; + private static final String secret = null; + private static final String bucketName = "com.overleaf.testbucket"; + private static final String region = "us-east-1"; - private S3SwapStore s3; + private S3SwapStore s3; - @Before - public void setup() { - if (accessKey == null || secret == null) { - s3 = null; - return; - } - s3 = new S3SwapStore(accessKey, secret, bucketName, region); + @Before + public void setup() { + if (accessKey == null || secret == null) { + s3 = null; + return; } + s3 = new S3SwapStore(accessKey, secret, bucketName, region); + } -// @Ignore -// @Test -// public void testUploadDownloadDelete() throws Exception { -// assumeNotNull(s3); -// String projName = "abc123"; -// byte[] contents = "hello".getBytes(); -// s3.upload( -// projName, -// new ByteArrayInputStream(contents), -// contents.length -// ); -// InputStream down = s3.openDownloadStream(projName); -// s3.remove(projName); -// assertArrayEquals(contents, IOUtils.toByteArray(down)); -// } + // @Ignore + // @Test + // public void testUploadDownloadDelete() throws Exception { + // assumeNotNull(s3); + // String projName = "abc123"; + // byte[] contents = "hello".getBytes(); + // s3.upload( + // projName, + // new ByteArrayInputStream(contents), + // contents.length + // ); + // InputStream down = s3.openDownloadStream(projName); + // s3.remove(projName); + // assertArrayEquals(contents, IOUtils.toByteArray(down)); + // } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/data/model/ResourceFetcherTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/data/model/ResourceFetcherTest.java index 625833b798..1f2796669c 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/data/model/ResourceFetcherTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/data/model/ResourceFetcherTest.java @@ -1,5 +1,13 @@ package uk.ac.ic.wlgitbridge.data.model; +import static org.junit.Assert.assertEquals; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Rule; @@ -16,66 +24,51 @@ import uk.ac.ic.wlgitbridge.bridge.resource.UrlResourceCache; import uk.ac.ic.wlgitbridge.data.filestore.RawFile; import uk.ac.ic.wlgitbridge.git.exception.GitUserException; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; - -/** +/* * Created by m on 20/11/15. */ public class ResourceFetcherTest { - @Rule - public MockServerRule mockServerRule = new MockServerRule(this); + @Rule public MockServerRule mockServerRule = new MockServerRule(this); - private MockServerClient mockServerClient; + private MockServerClient mockServerClient; - @Test - public void fetchesFilesThatAreMissingFromUrlStoreCache() throws IOException, GitUserException { - final String testProjectName = "123abc"; - final String testUrl = "http://localhost:" + mockServerRule.getPort() + "/123abc"; - final String oldTestPath = "testPath"; - final String newTestPath = "missingPath"; + @Test + public void fetchesFilesThatAreMissingFromUrlStoreCache() throws IOException, GitUserException { + final String testProjectName = "123abc"; + final String testUrl = "http://localhost:" + mockServerRule.getPort() + "/123abc"; + final String oldTestPath = "testPath"; + final String newTestPath = "missingPath"; - mockServerClient.when( - request() - .withMethod("GET") - .withPath("/123abc") - ) - .respond( - response() - .withStatusCode(200) - .withBody("content") - ); + mockServerClient + .when(request().withMethod("GET").withPath("/123abc")) + .respond(response().withStatusCode(200).withBody("content")); - final Mockery context = new Mockery(); - final DBStore dbStore = context.mock(DBStore.class); - context.checking(new Expectations() {{ + final Mockery context = new Mockery(); + final DBStore dbStore = context.mock(DBStore.class); + context.checking( + new Expectations() { + { // It should fetch the file once it finds it is missing. oneOf(dbStore).getPathForURLInProject(testProjectName, testUrl); will(returnValue(oldTestPath)); - // It should update the URL index store once it has fetched; at present, it does not actually change the stored path. + // It should update the URL index store once it has fetched; at present, it does not + // actually change the stored path. oneOf(dbStore).addURLIndexForProject(testProjectName, testUrl, oldTestPath); - }}); + } + }); - ResourceCache resources = new UrlResourceCache(dbStore); - TemporaryFolder repositoryFolder = new TemporaryFolder(); - repositoryFolder.create(); - String repoStorePath = repositoryFolder.getRoot().getAbsolutePath(); - RepoStore repoStore = new FSGitRepoStore(repoStorePath, Optional.empty()); - ProjectRepo repo = repoStore.initRepo("repo"); - Map fileTable = repo.getDirectory().getFileTable(); - Map fetchedUrls = new HashMap<>(); - resources.get( - testProjectName, testUrl, newTestPath, - fileTable, fetchedUrls, Optional.empty()); + ResourceCache resources = new UrlResourceCache(dbStore); + TemporaryFolder repositoryFolder = new TemporaryFolder(); + repositoryFolder.create(); + String repoStorePath = repositoryFolder.getRoot().getAbsolutePath(); + RepoStore repoStore = new FSGitRepoStore(repoStorePath, Optional.empty()); + ProjectRepo repo = repoStore.initRepo("repo"); + Map fileTable = repo.getDirectory().getFileTable(); + Map fetchedUrls = new HashMap<>(); + resources.get(testProjectName, testUrl, newTestPath, fileTable, fetchedUrls, Optional.empty()); - // We don't bother caching in this case, at present. - assertEquals(0, fetchedUrls.size()); - } + // We don't bother caching in this case, at present. + assertEquals(0, fetchedUrls.size()); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManagerTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManagerTest.java index 8575127064..fe2008c1df 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManagerTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/push/PostbackManagerTest.java @@ -1,59 +1,54 @@ package uk.ac.ic.wlgitbridge.snapshot.push; +import static org.junit.Assert.*; + import org.junit.Assert; import org.junit.Test; import uk.ac.ic.wlgitbridge.snapshot.push.exception.InternalErrorException; import uk.ac.ic.wlgitbridge.snapshot.push.exception.SnapshotPostException; import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException; -import static org.junit.Assert.*; - -/** +/* * Created by winston on 05/04/2016. */ public class PostbackManagerTest { - private final PostbackManager postbackManager = new PostbackManager(); + private final PostbackManager postbackManager = new PostbackManager(); - @Test - public void testRaceWithVersionId() - throws UnexpectedPostbackException, - SnapshotPostException { - String key = postbackManager.makeKeyForProject("proj"); - postbackManager.postVersionIDForProject("proj", 1, key); - int versionId = postbackManager.waitForVersionIdOrThrow("proj"); - assertEquals("Version id didn't match posted", 1, versionId); - } - - @Test - public void testRaceWithException() throws UnexpectedPostbackException, - SnapshotPostException { - String key = postbackManager.makeKeyForProject("proj"); - InternalErrorException ex = new InternalErrorException(); - postbackManager.postExceptionForProject("proj", ex, key); - try { - postbackManager.waitForVersionIdOrThrow("proj"); - } catch (InternalErrorException e) { - Assert.assertSame("Wrong exception was thrown", ex, e); - return; - } - Assert.fail("Exception wasn't thrown as required"); - } - - @Test - public void testTableConsistency() throws UnexpectedPostbackException, - SnapshotPostException { - String key1 = postbackManager.makeKeyForProject("proj1"); - assertEquals(1, postbackManager.postbackContentsTable.size()); - String key2 = postbackManager.makeKeyForProject("proj2"); - assertEquals(2, postbackManager.postbackContentsTable.size()); - postbackManager.postVersionIDForProject("proj1", 1, key1); - postbackManager.postVersionIDForProject("proj2", 1, key2); - assertEquals(2, postbackManager.postbackContentsTable.size()); - postbackManager.waitForVersionIdOrThrow("proj1"); - assertEquals(1, postbackManager.postbackContentsTable.size()); - postbackManager.waitForVersionIdOrThrow("proj2"); - Assert.assertTrue(postbackManager.postbackContentsTable.isEmpty()); + @Test + public void testRaceWithVersionId() throws UnexpectedPostbackException, SnapshotPostException { + String key = postbackManager.makeKeyForProject("proj"); + postbackManager.postVersionIDForProject("proj", 1, key); + int versionId = postbackManager.waitForVersionIdOrThrow("proj"); + assertEquals("Version id didn't match posted", 1, versionId); + } + + @Test + public void testRaceWithException() throws UnexpectedPostbackException, SnapshotPostException { + String key = postbackManager.makeKeyForProject("proj"); + InternalErrorException ex = new InternalErrorException(); + postbackManager.postExceptionForProject("proj", ex, key); + try { + postbackManager.waitForVersionIdOrThrow("proj"); + } catch (InternalErrorException e) { + Assert.assertSame("Wrong exception was thrown", ex, e); + return; } + Assert.fail("Exception wasn't thrown as required"); + } + @Test + public void testTableConsistency() throws UnexpectedPostbackException, SnapshotPostException { + String key1 = postbackManager.makeKeyForProject("proj1"); + assertEquals(1, postbackManager.postbackContentsTable.size()); + String key2 = postbackManager.makeKeyForProject("proj2"); + assertEquals(2, postbackManager.postbackContentsTable.size()); + postbackManager.postVersionIDForProject("proj1", 1, key1); + postbackManager.postVersionIDForProject("proj2", 1, key2); + assertEquals(2, postbackManager.postbackContentsTable.size()); + postbackManager.waitForVersionIdOrThrow("proj1"); + assertEquals(1, postbackManager.postbackContentsTable.size()); + postbackManager.waitForVersionIdOrThrow("proj2"); + Assert.assertTrue(postbackManager.postbackContentsTable.isEmpty()); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest.java index 12ead45330..532ccf7117 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest.java @@ -1,61 +1,130 @@ package uk.ac.ic.wlgitbridge.snapshot.servermock.util; -import org.junit.Assert; -import org.junit.Test; - import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import org.junit.Assert; +import org.junit.Test; public class FileUtilTest { - @Test - public void returnsTrueWhenFilesAreEqualInBothDirectories() throws URISyntaxException { - Path eq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenFilesAreEqualInBothDirectories/eq1").toURI()); - Path eq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenFilesAreEqualInBothDirectories/eq2").toURI()); - Assert.assertTrue(FileUtil.gitDirectoriesAreEqual(eq1, eq2)); - } + @Test + public void returnsTrueWhenFilesAreEqualInBothDirectories() throws URISyntaxException { + Path eq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenFilesAreEqualInBothDirectories/eq1") + .toURI()); + Path eq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenFilesAreEqualInBothDirectories/eq2") + .toURI()); + Assert.assertTrue(FileUtil.gitDirectoriesAreEqual(eq1, eq2)); + } - @Test - public void returnsTrueWhenRecursiveFilesAreEqualInBothDirectores() throws URISyntaxException { - Path eq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenRecursiveFilesAreEqualInBothDirectories/eq1").toURI()); - Path eq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenRecursiveFilesAreEqualInBothDirectories/eq2").toURI()); - Assert.assertTrue(FileUtil.gitDirectoriesAreEqual(eq1, eq2)); - } + @Test + public void returnsTrueWhenRecursiveFilesAreEqualInBothDirectores() throws URISyntaxException { + Path eq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenRecursiveFilesAreEqualInBothDirectories/eq1") + .toURI()); + Path eq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueWhenRecursiveFilesAreEqualInBothDirectories/eq2") + .toURI()); + Assert.assertTrue(FileUtil.gitDirectoriesAreEqual(eq1, eq2)); + } - @Test - public void returnsFalseWhenFilesAreNotEqualInBothDirectories() throws URISyntaxException { - Path neq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenFilesAreNotEqualInBothDirectories/neq1").toURI()); - Path neq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenFilesAreNotEqualInBothDirectories/neq2").toURI()); - Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); - } + @Test + public void returnsFalseWhenFilesAreNotEqualInBothDirectories() throws URISyntaxException { + Path neq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenFilesAreNotEqualInBothDirectories/neq1") + .toURI()); + Path neq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenFilesAreNotEqualInBothDirectories/neq2") + .toURI()); + Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); + } - @Test - public void returnsFalseWhenRecursiveFilesAreNotEqualInBothDirectories() throws URISyntaxException { - Path neq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenRecursiveFilesAreNotEqualInBothDirectories/neq1").toURI()); - Path neq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenRecursiveFilesAreNotEqualInBothDirectories/neq2").toURI()); - Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); - } + @Test + public void returnsFalseWhenRecursiveFilesAreNotEqualInBothDirectories() + throws URISyntaxException { + Path neq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenRecursiveFilesAreNotEqualInBothDirectories/neq1") + .toURI()); + Path neq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseWhenRecursiveFilesAreNotEqualInBothDirectories/neq2") + .toURI()); + Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); + } - @Test - public void returnsTrueEvenIfGitDirectoriesAreNotEqual() throws URISyntaxException { - Path neq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueEvenIfGitDirectoriesAreNotEqual/eq1").toURI()); - Path neq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueEvenIfGitDirectoriesAreNotEqual/eq2").toURI()); - Assert.assertTrue(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); - } + @Test + public void returnsTrueEvenIfGitDirectoriesAreNotEqual() throws URISyntaxException { + Path neq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueEvenIfGitDirectoriesAreNotEqual/eq1") + .toURI()); + Path neq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsTrueEvenIfGitDirectoriesAreNotEqual/eq2") + .toURI()); + Assert.assertTrue(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); + } - @Test - public void returnsFalseIfFileNamesAreNotEqual() throws URISyntaxException { - Path neq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfFileNamesAreNotEqual/neq1").toURI()); - Path neq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfFileNamesAreNotEqual/neq2").toURI()); - Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); - } + @Test + public void returnsFalseIfFileNamesAreNotEqual() throws URISyntaxException { + Path neq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfFileNamesAreNotEqual/neq1") + .toURI()); + Path neq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfFileNamesAreNotEqual/neq2") + .toURI()); + Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); + } - @Test - public void returnsFalseIfInnerDirectoryNamesAreNotEqual() throws URISyntaxException { - Path neq1 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfInnerDirectoryNamesAreNotEqual/neq1").toURI()); - Path neq2 = Paths.get(getClass().getResource("/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfInnerDirectoryNamesAreNotEqual/neq2").toURI()); - Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); - } - -} \ No newline at end of file + @Test + public void returnsFalseIfInnerDirectoryNamesAreNotEqual() throws URISyntaxException { + Path neq1 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfInnerDirectoryNamesAreNotEqual/neq1") + .toURI()); + Path neq2 = + Paths.get( + getClass() + .getResource( + "/uk/ac/ic/wlgitbridge/snapshot/servermock/util/FileUtilTest/returnsFalseIfInnerDirectoryNamesAreNotEqual/neq2") + .toURI()); + Assert.assertFalse(FileUtil.gitDirectoriesAreEqual(neq1, neq2)); + } +} diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/ProjectTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/ProjectTest.java index 23d664e4ed..ef17eda4e4 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/ProjectTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/ProjectTest.java @@ -3,16 +3,15 @@ package uk.ac.ic.wlgitbridge.util; import org.junit.Assert; import org.junit.Test; -/** +/* * Created by winston on 23/08/2016. */ public class ProjectTest { - @Test - public void testValidProjectNames() { - Assert.assertFalse(Project.isValidProjectName(null)); - Assert.assertFalse(Project.isValidProjectName("")); - Assert.assertFalse(Project.isValidProjectName(".wlgb")); - } - + @Test + public void testValidProjectNames() { + Assert.assertFalse(Project.isValidProjectName(null)); + Assert.assertFalse(Project.isValidProjectName("")); + Assert.assertFalse(Project.isValidProjectName(".wlgb")); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TarTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TarTest.java index 9834d7999e..59c00d5f45 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TarTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TarTest.java @@ -1,76 +1,70 @@ package uk.ac.ic.wlgitbridge.util; -import org.junit.Before; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.io.InputStream; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; -import static org.junit.Assert.assertTrue; - -/** +/* * Created by winston on 23/08/2016. */ public class TarTest { - private static final String RESOURCE_DIR - = "/uk/ac/ic/wlgitbridge/util/TarTest"; + private static final String RESOURCE_DIR = "/uk/ac/ic/wlgitbridge/util/TarTest"; - private File testDir; - private File dirWithEmptyFile; - private File tmpDir; + private File testDir; + private File dirWithEmptyFile; + private File tmpDir; - @Before - public void setup() throws IOException { - TemporaryFolder tmpFolder = new TemporaryFolder(); - tmpFolder.create(); - testDir = ResourceUtil.copyOfFolderResource( - RESOURCE_DIR + "/testdir", - tmpFolder::newFolder); - dirWithEmptyFile = ResourceUtil.copyOfFolderResource( - RESOURCE_DIR + "/dir_with_empty_file", - tmpFolder::newFolder); - tmpDir = tmpFolder.newFolder(); + @Before + public void setup() throws IOException { + TemporaryFolder tmpFolder = new TemporaryFolder(); + tmpFolder.create(); + testDir = ResourceUtil.copyOfFolderResource(RESOURCE_DIR + "/testdir", tmpFolder::newFolder); + dirWithEmptyFile = + ResourceUtil.copyOfFolderResource( + RESOURCE_DIR + "/dir_with_empty_file", tmpFolder::newFolder); + tmpDir = tmpFolder.newFolder(); + } + + /* + * Compresses inputDir and decompresses to outputDir. Checks equality + * between outputDir and inputDir. + * @param inputDir the directory to compress + * @param outputDir the output directory. Must be empty. + * @param compressFunction compression function + * @param decompressFunction decompression function + * @throws IOException + */ + private static void assertCompDecompEqual( + File inputDir, + File outputDir, + FunctionT compressFunction, + BiConsumerT decompressFunction) + throws IOException { + try (InputStream tarbz2 = compressFunction.apply(inputDir)) { + decompressFunction.accept(tarbz2, outputDir); + File unzipped = new File(outputDir, inputDir.getName()); + assertTrue(Files.contentsAreEqual(inputDir, unzipped)); } + } - /** - * Compresses inputDir and decompresses to outputDir. Checks equality - * between outputDir and inputDir. - * @param inputDir the directory to compress - * @param outputDir the output directory. Must be empty. - * @param compressFunction compression function - * @param decompressFunction decompression function - * @throws IOException - */ - private static void assertCompDecompEqual( - File inputDir, - File outputDir, - FunctionT compressFunction, - BiConsumerT decompressFunction - ) throws IOException { - try (InputStream tarbz2 = compressFunction.apply(inputDir)) { - decompressFunction.accept(tarbz2, outputDir); - File unzipped = new File(outputDir, inputDir.getName()); - assertTrue(Files.contentsAreEqual(inputDir, unzipped)); - } - } + @Test + public void tarAndUntarProducesTheSameResult() throws IOException { + assertCompDecompEqual(testDir, tmpDir, Tar::tar, Tar::untar); + } - @Test - public void tarAndUntarProducesTheSameResult() throws IOException { - assertCompDecompEqual(testDir, tmpDir, Tar::tar, Tar::untar); - } - - @Test - public void tarbz2AndUntarbz2ProducesTheSameResult() throws IOException { - assertCompDecompEqual(testDir, tmpDir, Tar.bz2::zip, Tar.bz2::unzip); - } - - @Test - public void tarbz2WorksOnDirectoriesWithAnEmptyFile() throws IOException { - assertCompDecompEqual( - dirWithEmptyFile, tmpDir, Tar.bz2::zip, Tar.bz2::unzip); - } + @Test + public void tarbz2AndUntarbz2ProducesTheSameResult() throws IOException { + assertCompDecompEqual(testDir, tmpDir, Tar.bz2::zip, Tar.bz2::unzip); + } + @Test + public void tarbz2WorksOnDirectoriesWithAnEmptyFile() throws IOException { + assertCompDecompEqual(dirWithEmptyFile, tmpDir, Tar.bz2::zip, Tar.bz2::unzip); + } } diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TimerUtilsTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TimerUtilsTest.java index 0a6b399372..892b9c1d7a 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TimerUtilsTest.java +++ b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/util/TimerUtilsTest.java @@ -1,19 +1,18 @@ package uk.ac.ic.wlgitbridge.util; -import org.junit.Test; - import static org.junit.Assert.assertEquals; -/** +import org.junit.Test; + +/* * Created by winston on 23/08/2016. */ public class TimerUtilsTest { - @Test - public void testMakeTimerTask() { - int[] iPtr = new int[] { 3 }; - TimerUtils.makeTimerTask(() -> iPtr[0] = 5).run(); - assertEquals(5, iPtr[0]); - } - + @Test + public void testMakeTimerTask() { + int[] iPtr = new int[] {3}; + TimerUtils.makeTimerTask(() -> iPtr[0] = 5).run(); + assertEquals(5, iPtr[0]); + } }