Wrote persistent store.

This commit is contained in:
Winston Li 2014-11-19 18:28:55 +00:00
parent dca42ac72b
commit 071e0a87cc
12 changed files with 343 additions and 56 deletions

View file

@ -9,7 +9,6 @@ public class Main {
public static void main(String[] args) { public static void main(String[] args) {
new WLGitBridgeApplication(args).run(); new WLGitBridgeApplication(args).run();
} }
} }

View file

@ -3,6 +3,7 @@ package uk.ac.ic.wlgitbridge.writelatex.filestore.node;
import uk.ac.ic.wlgitbridge.writelatex.api.request.exception.FailedConnectionException; import uk.ac.ic.wlgitbridge.writelatex.api.request.exception.FailedConnectionException;
import uk.ac.ic.wlgitbridge.writelatex.api.request.getforversion.SnapshotAttachment; import uk.ac.ic.wlgitbridge.writelatex.api.request.getforversion.SnapshotAttachment;
import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.AttachmentBlob; import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.AttachmentBlob;
import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.ByteBlob;
import uk.ac.ic.wlgitbridge.writelatex.filestore.store.FileIndexStore; import uk.ac.ic.wlgitbridge.writelatex.filestore.store.FileIndexStore;
import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.Blob; import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.Blob;
import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.ExternalBlob; import uk.ac.ic.wlgitbridge.writelatex.filestore.blob.ExternalBlob;
@ -23,13 +24,24 @@ public class AttachmentNode extends FileNode {
initBlob(fileIndexes); initBlob(fileIndexes);
} }
public AttachmentNode(String filePath, boolean changed, String url) {
super(filePath, changed);
this.url = url;
}
public AttachmentNode(String url, byte[] blob) {
super();
this.url = url;
this.blob = new ByteBlob(blob);
}
@Override @Override
public void handleIndexer(FileNodeIndexer fileNodeIndexer) { public void indexWith(FileNodeIndexer fileNodeIndexer) {
fileNodeIndexer.index(this); fileNodeIndexer.index(this);
} }
@Override @Override
protected Blob getBlob() { public Blob getBlob() {
return blob; return blob;
} }

View file

@ -28,8 +28,13 @@ public class BlobNode extends FileNode {
writeChanged(projectAttDirectory); writeChanged(projectAttDirectory);
} }
public BlobNode(String fileName, boolean changed, byte[] blob) {
super(fileName, changed);
this.blob = new ByteBlob(blob);
}
@Override @Override
public void handleIndexer(FileNodeIndexer fileNodeIndexer) { public void indexWith(FileNodeIndexer fileNodeIndexer) {
fileNodeIndexer.index(this); fileNodeIndexer.index(this);
} }

View file

@ -23,17 +23,19 @@ public abstract class FileNode {
} }
public FileNode(String filePath, Map<String, FileNode> context) { public FileNode(String filePath, Map<String, FileNode> context) {
this.filePath = filePath;
FileNode currentFileNode = context.get(filePath); FileNode currentFileNode = context.get(filePath);
this.filePath = filePath;
changed = currentFileNode == null || !equals(currentFileNode); changed = currentFileNode == null || !equals(currentFileNode);
} }
public String getFilePath() { protected FileNode(String filePath, boolean changed) {
return filePath; this.filePath = filePath;
this.changed = changed;
} }
public boolean isChanged() { protected FileNode() {
return changed; filePath = "";
changed = false;
} }
public byte[] getContents() throws FailedConnectionException { public byte[] getContents() throws FailedConnectionException {
@ -49,8 +51,15 @@ public abstract class FileNode {
out.close(); out.close();
} }
public abstract void handleIndexer(FileNodeIndexer fileNodeIndexer); public String getFilePath() {
return filePath;
}
public boolean isChanged() {
return changed;
}
public abstract void indexWith(FileNodeIndexer indexer);
protected abstract Blob getBlob(); protected abstract Blob getBlob();
@Override @Override

View file

@ -22,7 +22,7 @@ public class FileIndexStore implements FileNodeIndexer {
blobHashMappings = new HashMap<BlobHash, FileNode>(); blobHashMappings = new HashMap<BlobHash, FileNode>();
urlMappings = new HashMap<String, FileNode>(); urlMappings = new HashMap<String, FileNode>();
for (FileNode fileNode : fileNodes) { for (FileNode fileNode : fileNodes) {
fileNode.handleIndexer(this); fileNode.indexWith(this);
} }
} }

View file

@ -9,12 +9,11 @@ import uk.ac.ic.wlgitbridge.writelatex.api.request.exception.FailedConnectionExc
import uk.ac.ic.wlgitbridge.writelatex.api.request.getdoc.exception.InvalidProjectException; import uk.ac.ic.wlgitbridge.writelatex.api.request.getdoc.exception.InvalidProjectException;
import uk.ac.ic.wlgitbridge.writelatex.api.request.push.exception.SnapshotPostException; import uk.ac.ic.wlgitbridge.writelatex.api.request.push.exception.SnapshotPostException;
import uk.ac.ic.wlgitbridge.writelatex.filestore.store.WLFileStore; import uk.ac.ic.wlgitbridge.writelatex.filestore.store.WLFileStore;
import uk.ac.ic.wlgitbridge.writelatex.model.db.SQLiteWLDatabase; import uk.ac.ic.wlgitbridge.writelatex.model.db.PersistentStoreAPI;
import uk.ac.ic.wlgitbridge.writelatex.model.db.WLDatabase; import uk.ac.ic.wlgitbridge.writelatex.model.db.WLGBPersistentStore;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException;
import java.util.List; import java.util.List;
/** /**
@ -22,21 +21,15 @@ import java.util.List;
*/ */
public class WLDataModel implements CandidateSnapshotCallback { public class WLDataModel implements CandidateSnapshotCallback {
private WLDatabase db; private final PersistentStoreAPI persistentStore;
private final WLProjectStore projectStore; private final WLProjectStore projectStore;
private final WLFileStore fileStore; private final WLFileStore fileStore;
public WLDataModel(String rootGitDirectoryPath) { public WLDataModel(String rootGitDirectoryPath) {
File rootGitDirectory = initRootGitDirectory(rootGitDirectoryPath); File rootGitDirectory = initRootGitDirectory(rootGitDirectoryPath);
try { persistentStore = new WLGBPersistentStore(rootGitDirectory);
db = new SQLiteWLDatabase(rootGitDirectory); projectStore = persistentStore.loadProjectStore();
} catch (SQLException e) { fileStore = persistentStore.loadFileStore();
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
projectStore = db.loadProjectStore();
fileStore = db.loadFileStore();
} }
public List<WritableRepositoryContents> updateProjectWithName(String name) throws FailedConnectionException, InvalidProjectException { public List<WritableRepositoryContents> updateProjectWithName(String name) throws FailedConnectionException, InvalidProjectException {

View file

@ -1,6 +1,6 @@
package uk.ac.ic.wlgitbridge.writelatex.model; package uk.ac.ic.wlgitbridge.writelatex.model;
import uk.ac.ic.wlgitbridge.writelatex.model.db.WLDatabase; import uk.ac.ic.wlgitbridge.writelatex.model.db.PersistentStoreAPI;
import uk.ac.ic.wlgitbridge.writelatex.model.db.WLDatabaseSource; import uk.ac.ic.wlgitbridge.writelatex.model.db.WLDatabaseSource;
import java.util.HashMap; import java.util.HashMap;
@ -29,8 +29,7 @@ public class WLProjectStore implements WLDatabaseSource {
} }
@Override @Override
public void initFromDatabase(WLDatabase database) { public void initFromDatabase(PersistentStoreAPI database) {
} }
} }

View file

@ -0,0 +1,29 @@
package uk.ac.ic.wlgitbridge.writelatex.model.db;
import uk.ac.ic.wlgitbridge.writelatex.filestore.node.FileNode;
import uk.ac.ic.wlgitbridge.writelatex.filestore.store.WLFileStore;
import uk.ac.ic.wlgitbridge.writelatex.model.WLProjectStore;
import java.util.List;
import java.util.Map;
/**
* Created by Winston on 17/11/14.
*/
public interface PersistentStoreAPI {
public WLProjectStore loadProjectStore();
public WLFileStore loadFileStore();
public void addProject(String name);
public void addSnapshot(String projectName, int versionID);
public void addFileNodeBlob(String projectName, String fileName, int changed, byte[] blob);
public void addFileNodeExternal(String projectName, String fileName, int changed, String url);
public void addURLIndex(String projectName, String url, byte[] blob);
public List<String> getProjectNames();
public List<Integer> getVersionIDsForProjectName(String projectName);
public List<FileNode> getFileNodesForProjectName(String projectName);
public Map<String, FileNode> getURLIndexTableForProjectName(String projectName);
}

View file

@ -1,15 +1,20 @@
package uk.ac.ic.wlgitbridge.writelatex.model.db; package uk.ac.ic.wlgitbridge.writelatex.model.db;
import uk.ac.ic.wlgitbridge.writelatex.filestore.store.WLFileStore; import uk.ac.ic.wlgitbridge.writelatex.filestore.node.AttachmentNode;
import uk.ac.ic.wlgitbridge.writelatex.model.WLProjectStore; import uk.ac.ic.wlgitbridge.writelatex.filestore.node.BlobNode;
import uk.ac.ic.wlgitbridge.writelatex.filestore.node.FileNode;
import java.io.File; import java.io.File;
import java.sql.*; import java.sql.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/** /**
* Created by Winston on 17/11/14. * Created by Winston on 17/11/14.
*/ */
public class SQLiteWLDatabase implements WLDatabase { public class SQLiteWLDatabase {
private static final String[] CREATE_TABLE_STATEMENTS = { private static final String[] CREATE_TABLE_STATEMENTS = {
"CREATE TABLE IF NOT EXISTS `projects` (\n" + "CREATE TABLE IF NOT EXISTS `projects` (\n" +
@ -25,6 +30,7 @@ public class SQLiteWLDatabase implements WLDatabase {
"CREATE TABLE IF NOT EXISTS `file_node_table` (\n" + "CREATE TABLE IF NOT EXISTS `file_node_table` (\n" +
" `project_name` varchar(10) NOT NULL DEFAULT '',\n" + " `project_name` varchar(10) NOT NULL DEFAULT '',\n" +
" `file_name` varchar(255) NOT NULL DEFAULT '',\n" + " `file_name` varchar(255) NOT NULL DEFAULT '',\n" +
" `changed` tinyint(1) NOT NULL,\n" +
" `is_blob` tinyint(1) NOT NULL,\n" + " `is_blob` tinyint(1) NOT NULL,\n" +
" `blob` blob,\n" + " `blob` blob,\n" +
" `url` varchar(128) DEFAULT NULL,\n" + " `url` varchar(128) DEFAULT NULL,\n" +
@ -40,12 +46,39 @@ public class SQLiteWLDatabase implements WLDatabase {
")" ")"
}; };
private static final String addProject = private static final String ADD_PROJECT =
"INSERT INTO `projects` (`name`) VALUES (?);\n"; "INSERT INTO `projects` (`name`) VALUES (?);\n";
private static final String ADD_SNAPSHOT =
"INSERT INTO `snapshots` (`project_name`, `version_id`) VALUES (?, ?);\n";
private static final String ADD_FILE_NODE_BLOB =
"INSERT INTO `file_node_table` (`project_name`, `file_name`, `changed`, `is_blob`, `blob`, `url`) VALUES (?, ?, ?, '1', ?, NULL);\n";
private static final String ADD_FILE_NODE_EXTERNAL =
"INSERT INTO `file_node_table` (`project_name`, `file_name`, `changed`, `is_blob`, `blob`, `url`) VALUES (?, ?, ?, '0', NULL, ?);\n";
private static final String ADD_URL_INDEX =
"INSERT INTO `url_index_store` (`project_name`, `url`, `blob`) VALUES (?, ?, ?);\n";
private static final String GET_PROJECT_NAMES =
"SELECT * FROM `projects`;\n";
private static final String GET_VERSION_IDS_FOR_PROJECT_NAME =
"SELECT `version_id` FROM `snapshots` WHERE `project_name` = ?";
private static final String GET_FILE_NODES_FOR_PROJECT_NAME =
"SELECT `file_name`, `changed`, `is_blob`, `blob`, `url` FROM `file_node_table` WHERE `project_name` = ?";
private static final String GET_URL_INDEXES_FOR_PROJECT_NAME =
"SELECT `url`, `blob` FROM `url_index_store` WHERE `project_name` = ?";
private final File rootGitDirectory; private final File rootGitDirectory;
private final Connection connection; private final Connection connection;
private PreparedStatement addProjectStatement; private PreparedStatement addProjectStatement;
private PreparedStatement addSnapshotStatement;
private PreparedStatement addFileNodeBlobStatement;
private PreparedStatement addFileNodeExternalStatement;
private PreparedStatement addURLIndexStatement;
private PreparedStatement getProjectNamesStatement;
private PreparedStatement getVersionIDsForProjectNameStatement;
private PreparedStatement getFileNodesForProjectNameStatement;
private PreparedStatement getURLIndexesForProjectNameStatement;
public SQLiteWLDatabase(File rootGitDirectory) throws SQLException, ClassNotFoundException { public SQLiteWLDatabase(File rootGitDirectory) throws SQLException, ClassNotFoundException {
this.rootGitDirectory = rootGitDirectory; this.rootGitDirectory = rootGitDirectory;
@ -55,7 +88,6 @@ public class SQLiteWLDatabase implements WLDatabase {
connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getAbsolutePath()); connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getAbsolutePath());
createTables(); createTables();
prepareStatements(); prepareStatements();
test();
} }
private void createTables() throws SQLException { private void createTables() throws SQLException {
@ -66,28 +98,129 @@ public class SQLiteWLDatabase implements WLDatabase {
} }
private void prepareStatements() throws SQLException { private void prepareStatements() throws SQLException {
addProjectStatement = connection.prepareStatement(addProject); addProjectStatement = connection.prepareStatement(ADD_PROJECT);
addSnapshotStatement = connection.prepareStatement(ADD_SNAPSHOT);
addFileNodeBlobStatement = connection.prepareStatement(ADD_FILE_NODE_BLOB);
addFileNodeExternalStatement = connection.prepareStatement(ADD_FILE_NODE_EXTERNAL);
addURLIndexStatement = connection.prepareStatement(ADD_URL_INDEX);
getProjectNamesStatement = connection.prepareStatement(GET_PROJECT_NAMES);
getVersionIDsForProjectNameStatement = connection.prepareStatement(GET_VERSION_IDS_FOR_PROJECT_NAME);
getFileNodesForProjectNameStatement = connection.prepareStatement(GET_FILE_NODES_FOR_PROJECT_NAME);
getURLIndexesForProjectNameStatement = connection.prepareStatement(GET_URL_INDEXES_FOR_PROJECT_NAME);
} }
public void addProject(String name) throws SQLException { public void addProject(String name) throws SQLException {
addProjectStatement.clearParameters();
addProjectStatement.setString(1, name); addProjectStatement.setString(1, name);
addProjectStatement.executeUpdate(); addProjectStatement.executeUpdate();
addProjectStatement.clearParameters(); }
public void addSnapshot(String projectName, int versionID) throws SQLException {
addSnapshotStatement.clearParameters();
addSnapshotStatement.setString(1, projectName);
addSnapshotStatement.setInt(2, versionID);
addSnapshotStatement.executeUpdate();
}
public void addFileNodeBlob(String projectName, String fileName, int changed, byte[] blob) throws SQLException {
addFileNodeBlobStatement.clearParameters();
addFileNodeBlobStatement.setString(1, projectName);
addFileNodeBlobStatement.setString(2, fileName);
addFileNodeBlobStatement.setInt(3, changed);
addFileNodeBlobStatement.setBytes(4, blob);
addFileNodeBlobStatement.executeUpdate();
}
public void addFileNodeExternal(String projectName, String fileName, int changed, String url) throws SQLException {
addFileNodeExternalStatement.clearParameters();
addFileNodeExternalStatement.setString(1, projectName);
addFileNodeExternalStatement.setString(2, fileName);
addFileNodeExternalStatement.setInt(3, changed);
addFileNodeExternalStatement.setString(4, url);
addFileNodeExternalStatement.executeUpdate();
}
public void addURLIndex(String projectName, String url, byte[] blob) throws SQLException {
addURLIndexStatement.clearParameters();
addURLIndexStatement.setString(1, projectName);
addURLIndexStatement.setString(2, url);
addURLIndexStatement.setBytes(3, blob);
addURLIndexStatement.executeUpdate();
}
public List<String> getProjectNames() throws SQLException {
List<String> projectNames = new LinkedList<String>();
ResultSet results = getProjectNamesStatement.executeQuery();
while (results.next()) {
projectNames.add(results.getString("name"));
}
return projectNames;
}
public List<Integer> getVersionIDsForProjectName(String projectName) throws SQLException {
List<Integer> versionIDs = new LinkedList<Integer>();
getVersionIDsForProjectNameStatement.clearParameters();
getVersionIDsForProjectNameStatement.setString(1, projectName);
ResultSet results = getVersionIDsForProjectNameStatement.executeQuery();
while (results.next()) {
versionIDs.add(results.getInt("version_id"));
}
return versionIDs;
}
public List<FileNode> getFileNodesForProjectName(String projectName) throws SQLException {
List<FileNode> fileNodes = new LinkedList<FileNode>();
getFileNodesForProjectNameStatement.clearParameters();
getFileNodesForProjectNameStatement.setString(1, projectName);
ResultSet results = getFileNodesForProjectNameStatement.executeQuery();
while (results.next()) {
boolean isBlob = intToBoolean(results.getInt("is_blob"));
FileNode fileNode;
String fileName = results.getString("file_name");
boolean changed = intToBoolean(results.getInt("changed"));
if (isBlob) {
fileNode = new BlobNode(fileName, changed, results.getBytes("blob"));
} else {
fileNode = new AttachmentNode(fileName, changed, results.getString("url"));
}
fileNodes.add(fileNode);
}
return fileNodes;
}
public Map<String, FileNode> getURLIndexTableForProjectName(String projectName) throws SQLException {
Map<String, FileNode> urlIndexTable = new HashMap<String, FileNode>();
getURLIndexesForProjectNameStatement.clearParameters();
getURLIndexesForProjectNameStatement.setString(1, projectName);
ResultSet results = getURLIndexesForProjectNameStatement.executeQuery();
while (results.next()) {
String url = results.getString("url");
byte[] blob = results.getBytes("blob");
urlIndexTable.put(url, new AttachmentNode(url, blob));
}
return urlIndexTable;
} }
private void test() throws SQLException { private void test() throws SQLException {
addProject("testproj12"); addProject("testproj12");
addSnapshot("testproj12", 0);
addSnapshot("testproj12", 1);
addFileNodeBlob("testproj12", "filename.tex", 1, "hello".getBytes());
addFileNodeExternal("testproj12", "urlname.jpg", 1, "http://someurl.com/urlname.jpg");
addURLIndex("testproj12", "http://someurl.com/urlname.jpg", "thebytes".getBytes());
} }
@Override private static int booleanToInt(boolean b) {
public WLProjectStore loadProjectStore() { if (b) {
return new WLProjectStore(); return 1;
} else {
return 0;
}
} }
@Override private static boolean intToBoolean(int i) {
public WLFileStore loadFileStore() { return i != 0;
return new WLFileStore(rootGitDirectory);
} }
} }

View file

@ -1,14 +0,0 @@
package uk.ac.ic.wlgitbridge.writelatex.model.db;
import uk.ac.ic.wlgitbridge.writelatex.filestore.store.WLFileStore;
import uk.ac.ic.wlgitbridge.writelatex.model.WLProjectStore;
/**
* Created by Winston on 17/11/14.
*/
public interface WLDatabase {
public WLProjectStore loadProjectStore();
public WLFileStore loadFileStore();
}

View file

@ -5,6 +5,6 @@ package uk.ac.ic.wlgitbridge.writelatex.model.db;
*/ */
public interface WLDatabaseSource { public interface WLDatabaseSource {
public void initFromDatabase(WLDatabase database); public void initFromDatabase(PersistentStoreAPI database);
} }

View file

@ -0,0 +1,122 @@
package uk.ac.ic.wlgitbridge.writelatex.model.db;
import uk.ac.ic.wlgitbridge.writelatex.filestore.node.FileNode;
import uk.ac.ic.wlgitbridge.writelatex.filestore.store.WLFileStore;
import uk.ac.ic.wlgitbridge.writelatex.model.WLProjectStore;
import java.io.File;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* Created by Winston on 19/11/14.
*/
public class WLGBPersistentStore implements PersistentStoreAPI {
private final File rootGitDirectory;
private final SQLiteWLDatabase database;
public WLGBPersistentStore(File rootGitDirectory) {
this.rootGitDirectory = rootGitDirectory;
try {
database = new SQLiteWLDatabase(rootGitDirectory);
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public WLProjectStore loadProjectStore() {
return new WLProjectStore();
}
@Override
public WLFileStore loadFileStore() {
return new WLFileStore(rootGitDirectory);
}
@Override
public void addProject(String name) {
try {
database.addProject(name);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void addSnapshot(String projectName, int versionID) {
try {
database.addSnapshot(projectName, versionID);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void addFileNodeBlob(String projectName, String fileName, int changed, byte[] blob) {
try {
database.addFileNodeBlob(projectName, fileName, changed, blob);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void addFileNodeExternal(String projectName, String fileName, int changed, String url) {
try {
database.addFileNodeExternal(projectName, fileName, changed, url);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void addURLIndex(String projectName, String url, byte[] blob) {
try {
database.addURLIndex(projectName, url, blob);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public List<String> getProjectNames() {
try {
return database.getProjectNames();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public List<Integer> getVersionIDsForProjectName(String projectName) {
try {
return database.getVersionIDsForProjectName(projectName);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public List<FileNode> getFileNodesForProjectName(String projectName) {
try {
return database.getFileNodesForProjectName(projectName);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Map<String, FileNode> getURLIndexTableForProjectName(String projectName) {
try {
return database.getURLIndexTableForProjectName(projectName);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}