package uk.ac.ic.wlgitbridge.bridge;
import com.google.api.client.auth.oauth2.Credential;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import uk.ac.ic.wlgitbridge.data.CandidateSnapshot;
import uk.ac.ic.wlgitbridge.data.ProjectLock;
import uk.ac.ic.wlgitbridge.data.ShutdownHook;
import uk.ac.ic.wlgitbridge.data.filestore.RawDirectory;
import uk.ac.ic.wlgitbridge.data.model.DataStore;
import uk.ac.ic.wlgitbridge.snapshot.base.ForbiddenException;
import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException;
import uk.ac.ic.wlgitbridge.snapshot.getdoc.GetDocRequest;
import uk.ac.ic.wlgitbridge.snapshot.getdoc.exception.InvalidProjectException;
import uk.ac.ic.wlgitbridge.snapshot.push.PostbackManager;
import uk.ac.ic.wlgitbridge.snapshot.push.PushRequest;
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.IOException;
/**
* Created by Winston on 16/11/14.
*/
public class BridgeAPI {
private final DataStore dataStore;
private final PostbackManager postbackManager;
private final ProjectLock mainProjectLock;
public BridgeAPI(String rootGitDirectoryPath) {
dataStore = new DataStore(rootGitDirectoryPath);
postbackManager = new PostbackManager();
mainProjectLock = new ProjectLock();
Runtime.getRuntime().addShutdownHook(new ShutdownHook(mainProjectLock));
}
public void lockForProject(String projectName) {
mainProjectLock.lockForProject(projectName);
}
public void unlockForProject(String projectName) {
mainProjectLock.unlockForProject(projectName);
}
public boolean repositoryExists(Credential oauth2, String projectName)
throws ServiceMayNotContinueException, ForbiddenException {
lockForProject(projectName);
GetDocRequest getDocRequest = new GetDocRequest(oauth2, projectName);
getDocRequest.request();
try {
getDocRequest.getResult().getVersionID();
} catch (InvalidProjectException e) {
return false;
} catch (FailedConnectionException e) {
throw e;
} catch (SnapshotPostException e) {
throw new ServiceMayNotContinueException(e.getMessage());
} finally {
unlockForProject(projectName);
}
return true;
}
public void getWritableRepositories(Credential oauth2,
String projectName,
Repository repository)
throws IOException,
SnapshotPostException,
GitAPIException,
ForbiddenException {
Log.info("[{}] Fetching", projectName);
dataStore.updateProjectWithName(oauth2, projectName, repository);
}
public void
putDirectoryContentsToProjectWithName(Credential oauth2,
String projectName,
RawDirectory directoryContents,
RawDirectory oldDirectoryContents,
String hostname)
throws SnapshotPostException, IOException, ForbiddenException {
mainProjectLock.lockForProject(projectName);
CandidateSnapshot candidate = null;
try {
Log.info("[{}] Pushing", projectName);
String postbackKey = postbackManager.makeKeyForProject(projectName);
Log.info(
"[{}] Created postback key: {}",
projectName,
postbackKey
);
candidate =
dataStore.createCandidateSnapshot(
projectName,
directoryContents,
oldDirectoryContents
);
Log.info(
"[{}] Candindate snapshot created: {}",
projectName,
candidate
);
PushRequest pushRequest = new PushRequest(
oauth2,
candidate,
postbackKey
);
pushRequest.request();
PushResult result = pushRequest.getResult();
if (result.wasSuccessful()) {
Log.info(
"[{}] Push to Overleaf successful",
projectName
);
Log.info("[{}] Waiting for postback...", projectName);
int versionID =
postbackManager.waitForVersionIdOrThrow(projectName);
Log.info(
"[{}] Got version ID for push: {}",
projectName,
versionID
);
dataStore.approveSnapshot(versionID, candidate);
Log.info(
"[{}] Approved version ID: {}",
projectName,
versionID
);
} else {
Log.warn(
"[{}] Went out of date while waiting for push",
projectName
);
throw new OutOfDateException();
}
} 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);
throw e;
} finally {
if (candidate != null) {
candidate.deleteServletFiles();
} else {
Log.error(
"[{}] Candidate snapshot was null: " +
"this should never happen.",
projectName
);
}
mainProjectLock.unlockForProject(projectName);
}
}
public void checkPostbackKey(String projectName, String postbackKey)
throws InvalidPostbackKeyException {
postbackManager.checkPostbackKey(projectName, postbackKey);
}
/* Called by postback thread. */
public void postbackReceivedSuccessfully(String projectName,
String postbackKey,
int versionID)
throws UnexpectedPostbackException {
Log.info(
"[{}]" +
" Postback received by postback thread, version: {}",
projectName,
versionID);
postbackManager.postVersionIDForProject(
projectName,
versionID,
postbackKey
);
}
public void postbackReceivedWithException(String projectName,
String postbackKey,
SnapshotPostException exception)
throws UnexpectedPostbackException {
Log.warn("[{}] Postback received with exception", projectName);
postbackManager.postExceptionForProject(
projectName,
exception,
postbackKey
);
}
}