mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-02 03:26:06 +01:00
Add lock for repository operation.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import util.Directory._
|
import util.Directory._
|
||||||
import util.{JGitUtil, UsersAuthenticator, ReferrerAuthenticator}
|
import util.{LockUtil, JGitUtil, UsersAuthenticator, ReferrerAuthenticator}
|
||||||
import service._
|
import service._
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
@@ -48,119 +48,125 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
* Create new repository.
|
* Create new repository.
|
||||||
*/
|
*/
|
||||||
post("/new", newForm)(usersOnly { form =>
|
post("/new", newForm)(usersOnly { form =>
|
||||||
val ownerAccount = getAccountByUserName(form.owner).get
|
LockUtil.lock(s"${form.owner}/${form.name}/create"){
|
||||||
val loginAccount = context.loginAccount.get
|
if(getRepository(form.owner, form.name, baseUrl).isEmpty){
|
||||||
val loginUserName = loginAccount.userName
|
val ownerAccount = getAccountByUserName(form.owner).get
|
||||||
|
val loginAccount = context.loginAccount.get
|
||||||
|
val loginUserName = loginAccount.userName
|
||||||
|
|
||||||
// Insert to the database at first
|
// Insert to the database at first
|
||||||
createRepository(form.name, form.owner, form.description, form.isPrivate)
|
createRepository(form.name, form.owner, form.description, form.isPrivate)
|
||||||
|
|
||||||
// Add collaborators for group repository
|
// Add collaborators for group repository
|
||||||
if(ownerAccount.isGroupAccount){
|
if(ownerAccount.isGroupAccount){
|
||||||
getGroupMembers(form.owner).foreach { userName =>
|
getGroupMembers(form.owner).foreach { userName =>
|
||||||
addCollaborator(form.owner, form.name, userName)
|
addCollaborator(form.owner, form.name, userName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert default labels
|
||||||
|
insertDefaultLabels(loginUserName, form.name)
|
||||||
|
|
||||||
|
// Create the actual repository
|
||||||
|
val gitdir = getRepositoryDir(form.owner, form.name)
|
||||||
|
JGitUtil.initRepository(gitdir)
|
||||||
|
|
||||||
|
if(form.createReadme){
|
||||||
|
val tmpdir = getInitRepositoryDir(form.owner, form.name)
|
||||||
|
try {
|
||||||
|
// Clone the repository
|
||||||
|
Git.cloneRepository.setURI(gitdir.toURI.toString).setDirectory(tmpdir).call
|
||||||
|
|
||||||
|
// Create README.md
|
||||||
|
FileUtils.writeStringToFile(new File(tmpdir, "README.md"),
|
||||||
|
if(form.description.nonEmpty){
|
||||||
|
form.name + "\n" +
|
||||||
|
"===============\n" +
|
||||||
|
"\n" +
|
||||||
|
form.description.get
|
||||||
|
} else {
|
||||||
|
form.name + "\n" +
|
||||||
|
"===============\n"
|
||||||
|
}, "UTF-8")
|
||||||
|
|
||||||
|
val git = Git.open(tmpdir)
|
||||||
|
git.add.addFilepattern("README.md").call
|
||||||
|
git.commit
|
||||||
|
.setCommitter(new PersonIdent(loginUserName, loginAccount.mailAddress))
|
||||||
|
.setMessage("Initial commit").call
|
||||||
|
git.push.call
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
FileUtils.deleteDirectory(tmpdir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Wiki repository
|
||||||
|
createWikiRepository(loginAccount, form.owner, form.name)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
recordCreateRepositoryActivity(form.owner, form.name, loginUserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redirect to the repository
|
||||||
|
redirect(s"/${form.owner}/${form.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert default labels
|
|
||||||
insertDefaultLabels(loginUserName, form.name)
|
|
||||||
|
|
||||||
// Create the actual repository
|
|
||||||
val gitdir = getRepositoryDir(form.owner, form.name)
|
|
||||||
JGitUtil.initRepository(gitdir)
|
|
||||||
|
|
||||||
if(form.createReadme){
|
|
||||||
val tmpdir = getInitRepositoryDir(form.owner, form.name)
|
|
||||||
try {
|
|
||||||
// Clone the repository
|
|
||||||
Git.cloneRepository.setURI(gitdir.toURI.toString).setDirectory(tmpdir).call
|
|
||||||
|
|
||||||
// Create README.md
|
|
||||||
FileUtils.writeStringToFile(new File(tmpdir, "README.md"),
|
|
||||||
if(form.description.nonEmpty){
|
|
||||||
form.name + "\n" +
|
|
||||||
"===============\n" +
|
|
||||||
"\n" +
|
|
||||||
form.description.get
|
|
||||||
} else {
|
|
||||||
form.name + "\n" +
|
|
||||||
"===============\n"
|
|
||||||
}, "UTF-8")
|
|
||||||
|
|
||||||
val git = Git.open(tmpdir)
|
|
||||||
git.add.addFilepattern("README.md").call
|
|
||||||
git.commit
|
|
||||||
.setCommitter(new PersonIdent(loginUserName, loginAccount.mailAddress))
|
|
||||||
.setMessage("Initial commit").call
|
|
||||||
git.push.call
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
FileUtils.deleteDirectory(tmpdir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Wiki repository
|
|
||||||
createWikiRepository(loginAccount, form.owner, form.name)
|
|
||||||
|
|
||||||
// Record activity
|
|
||||||
recordCreateRepositoryActivity(form.owner, form.name, loginUserName)
|
|
||||||
|
|
||||||
// redirect to the repository
|
|
||||||
redirect(s"/${form.owner}/${form.name}")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/_fork")(referrersOnly { repository =>
|
post("/:owner/:repository/_fork")(referrersOnly { repository =>
|
||||||
val loginAccount = context.loginAccount.get
|
val loginAccount = context.loginAccount.get
|
||||||
val loginUserName = loginAccount.userName
|
val loginUserName = loginAccount.userName
|
||||||
|
|
||||||
if(getRepository(loginUserName, repository.name, baseUrl).isEmpty){
|
LockUtil.lock(s"${loginUserName}/${repository.name}/create"){
|
||||||
// Insert to the database at first
|
if(getRepository(loginUserName, repository.name, baseUrl).isEmpty){
|
||||||
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
// Insert to the database at first
|
||||||
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
|
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
||||||
|
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
|
||||||
|
|
||||||
createRepository(
|
createRepository(
|
||||||
repositoryName = repository.name,
|
repositoryName = repository.name,
|
||||||
userName = loginUserName,
|
userName = loginUserName,
|
||||||
description = repository.repository.description,
|
description = repository.repository.description,
|
||||||
isPrivate = repository.repository.isPrivate,
|
isPrivate = repository.repository.isPrivate,
|
||||||
originRepositoryName = Some(originRepositoryName),
|
originRepositoryName = Some(originRepositoryName),
|
||||||
originUserName = Some(originUserName),
|
originUserName = Some(originUserName),
|
||||||
parentRepositoryName = Some(repository.name),
|
parentRepositoryName = Some(repository.name),
|
||||||
parentUserName = Some(repository.owner)
|
parentUserName = Some(repository.owner)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Insert default labels
|
// Insert default labels
|
||||||
insertDefaultLabels(loginUserName, repository.name)
|
insertDefaultLabels(loginUserName, repository.name)
|
||||||
|
|
||||||
// clone repository actually
|
// clone repository actually
|
||||||
JGitUtil.cloneRepository(
|
JGitUtil.cloneRepository(
|
||||||
getRepositoryDir(repository.owner, repository.name),
|
getRepositoryDir(repository.owner, repository.name),
|
||||||
getRepositoryDir(loginUserName, repository.name))
|
getRepositoryDir(loginUserName, repository.name))
|
||||||
|
|
||||||
// Create Wiki repository
|
// Create Wiki repository
|
||||||
JGitUtil.cloneRepository(
|
JGitUtil.cloneRepository(
|
||||||
getWikiRepositoryDir(repository.owner, repository.name),
|
getWikiRepositoryDir(repository.owner, repository.name),
|
||||||
getWikiRepositoryDir(loginUserName, repository.name))
|
getWikiRepositoryDir(loginUserName, repository.name))
|
||||||
|
|
||||||
// insert commit id
|
// insert commit id
|
||||||
JGitUtil.withGit(getRepositoryDir(loginUserName, repository.name)){ git =>
|
JGitUtil.withGit(getRepositoryDir(loginUserName, repository.name)){ git =>
|
||||||
JGitUtil.getRepositoryInfo(loginUserName, repository.name, baseUrl).branchList.foreach { branch =>
|
JGitUtil.getRepositoryInfo(loginUserName, repository.name, baseUrl).branchList.foreach { branch =>
|
||||||
JGitUtil.getCommitLog(git, branch) match {
|
JGitUtil.getCommitLog(git, branch) match {
|
||||||
case Right((commits, _)) => commits.foreach { commit =>
|
case Right((commits, _)) => commits.foreach { commit =>
|
||||||
if(!existsCommitId(loginUserName, repository.name, commit.id)){
|
if(!existsCommitId(loginUserName, repository.name, commit.id)){
|
||||||
insertCommitId(loginUserName, repository.name, commit.id)
|
insertCommitId(loginUserName, repository.name, commit.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
case Left(_) => ???
|
||||||
}
|
}
|
||||||
case Left(_) => ???
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Record activity
|
// Record activity
|
||||||
recordForkActivity(repository.owner, repository.name, loginUserName)
|
recordForkActivity(repository.owner, repository.name, loginUserName)
|
||||||
|
}
|
||||||
|
// redirect to the repository
|
||||||
|
redirect("/%s/%s".format(loginUserName, repository.name))
|
||||||
}
|
}
|
||||||
// redirect to the repository
|
|
||||||
redirect("/%s/%s".format(loginUserName, repository.name))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
private def insertDefaultLabels(userName: String, repositoryName: String): Unit = {
|
private def insertDefaultLabels(userName: String, repositoryName: String): Unit = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import util.{CollaboratorsAuthenticator, JGitUtil, ReferrerAuthenticator}
|
import util.{LockUtil, CollaboratorsAuthenticator, JGitUtil, ReferrerAuthenticator}
|
||||||
import util.Directory._
|
import util.Directory._
|
||||||
import util.Implicits._
|
import util.Implicits._
|
||||||
import util.JGitUtil.{DiffInfo, CommitInfo}
|
import util.JGitUtil.{DiffInfo, CommitInfo}
|
||||||
@@ -82,77 +82,85 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/pulls/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
|
post("/:owner/:repository/pulls/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
|
||||||
val issueId = params("id").toInt
|
LockUtil.lock(s"${repository.owner}/${repository.name}/merge"){
|
||||||
|
val issueId = params("id").toInt
|
||||||
|
|
||||||
getPullRequest(repository.owner, repository.name, issueId).map { case (issue, pullreq) =>
|
getPullRequest(repository.owner, repository.name, issueId).map { case (issue, pullreq) =>
|
||||||
val remote = getRepositoryDir(repository.owner, repository.name)
|
val remote = getRepositoryDir(repository.owner, repository.name)
|
||||||
val tmpdir = new java.io.File(getTemporaryDir(repository.owner, repository.name), s"merge-${issueId}")
|
val tmpdir = new java.io.File(getTemporaryDir(repository.owner, repository.name), s"merge-${issueId}")
|
||||||
|
val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).call
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO mark issue as 'merged'
|
||||||
|
val loginAccount = context.loginAccount.get
|
||||||
|
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Closed", "close")
|
||||||
|
updateClosed(repository.owner, repository.name, issueId, true)
|
||||||
|
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, form.message)
|
||||||
|
|
||||||
|
git.checkout.setName(pullreq.branch).call
|
||||||
|
|
||||||
|
git.fetch
|
||||||
|
.setRemote(getRepositoryDir(repository.owner, repository.name).toURI.toString)
|
||||||
|
.setRefSpecs(new RefSpec(s"refs/pull/${issueId}/head:refs/heads/${pullreq.branch}")).call
|
||||||
|
|
||||||
|
val result = git.merge
|
||||||
|
.include(git.getRepository.resolve("FETCH_HEAD"))
|
||||||
|
.setCommit(false).call
|
||||||
|
|
||||||
|
if(result.getConflicts != null){
|
||||||
|
throw new RuntimeException("This pull request can't merge automatically.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO merge commit
|
||||||
|
// git.commit
|
||||||
|
// .setCommitter(new PersonIdent(loginAccount.userName, loginAccount.mailAddress))
|
||||||
|
// .setMessage(s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestRepositoryName}\n"
|
||||||
|
// + form.message).call
|
||||||
|
git.push.call
|
||||||
|
|
||||||
|
val (commits, _) = getRequestCompareInfo(repository.owner, repository.name, pullreq.commitIdFrom,
|
||||||
|
pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo)
|
||||||
|
|
||||||
|
commits.flatten.foreach { commit =>
|
||||||
|
insertCommitId(repository.owner, repository.name, commit.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect(s"/${repository.owner}/${repository.name}/pulls/${issueId}")
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
git.getRepository.close
|
||||||
|
FileUtils.deleteDirectory(tmpdir)
|
||||||
|
}
|
||||||
|
} getOrElse NotFound
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
private def checkConflict(userName: String, repositoryName: String, branch: String,
|
||||||
|
requestUserName: String, requestRepositoryName: String, requestBranch: String): Boolean = {
|
||||||
|
LockUtil.lock(s"${userName}/${repositoryName}/merge-check"){
|
||||||
|
val remote = getRepositoryDir(userName, repositoryName)
|
||||||
|
val tmpdir = new java.io.File(getTemporaryDir(userName, repositoryName), "merge-check")
|
||||||
val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).call
|
val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).call
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO mark issue as 'merged'
|
if(tmpdir.exists()){
|
||||||
val loginAccount = context.loginAccount.get
|
FileUtils.deleteDirectory(tmpdir)
|
||||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Closed", "close")
|
}
|
||||||
updateClosed(repository.owner, repository.name, issueId, true)
|
git.checkout.setName(branch).call
|
||||||
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, form.message)
|
|
||||||
|
|
||||||
git.checkout.setName(pullreq.branch).call
|
|
||||||
|
|
||||||
git.fetch
|
git.fetch
|
||||||
.setRemote(getRepositoryDir(repository.owner, repository.name).toURI.toString)
|
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
||||||
.setRefSpecs(new RefSpec(s"refs/pull/${issueId}/head:refs/heads/${pullreq.branch}")).call
|
.setRefSpecs(new RefSpec(s"refs/heads/${branch}:refs/heads/${requestBranch}")).call
|
||||||
|
|
||||||
val result = git.merge
|
val result = git.merge
|
||||||
.include(git.getRepository.resolve("FETCH_HEAD"))
|
.include(git.getRepository.resolve("FETCH_HEAD"))
|
||||||
.setCommit(false).call
|
.setCommit(false).call
|
||||||
|
|
||||||
if(result.getConflicts != null){
|
result.getConflicts != null
|
||||||
throw new RuntimeException("This pull request can't merge automatically.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// git.commit
|
|
||||||
// .setCommitter(new PersonIdent(loginAccount.userName, loginAccount.mailAddress))
|
|
||||||
// .setMessage(s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestRepositoryName}\n"
|
|
||||||
// + form.message).call
|
|
||||||
git.push.call
|
|
||||||
|
|
||||||
val (commits, _) = getRequestCompareInfo(repository.owner, repository.name, pullreq.commitIdFrom,
|
|
||||||
pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo)
|
|
||||||
|
|
||||||
commits.flatten.foreach { commit =>
|
|
||||||
insertCommitId(repository.owner, repository.name, commit.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
redirect(s"/${repository.owner}/${repository.name}/pulls/${issueId}")
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
git.getRepository.close
|
git.getRepository.close
|
||||||
FileUtils.deleteDirectory(tmpdir)
|
FileUtils.deleteDirectory(tmpdir)
|
||||||
}
|
}
|
||||||
} getOrElse NotFound
|
|
||||||
})
|
|
||||||
|
|
||||||
private def checkConflict(userName: String, repositoryName: String, branch: String,
|
|
||||||
requestUserName: String, requestRepositoryName: String, requestBranch: String): Boolean = {
|
|
||||||
val remote = getRepositoryDir(userName, repositoryName)
|
|
||||||
val tmpdir = new java.io.File(getTemporaryDir(userName, repositoryName), "merge-check")
|
|
||||||
val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).call
|
|
||||||
try {
|
|
||||||
git.checkout.setName(branch).call
|
|
||||||
|
|
||||||
git.fetch
|
|
||||||
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
|
||||||
.setRefSpecs(new RefSpec(s"refs/heads/${branch}:refs/heads/${requestBranch}")).call
|
|
||||||
|
|
||||||
val result = git.merge
|
|
||||||
.include(git.getRepository.resolve("FETCH_HEAD"))
|
|
||||||
.setCommit(false).call
|
|
||||||
|
|
||||||
result.getConflicts != null
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
git.getRepository.close
|
|
||||||
FileUtils.deleteDirectory(tmpdir)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,9 +105,10 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/:page/_delete")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/wiki/:page/_delete")(collaboratorsOnly { repository =>
|
||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
|
val account = context.loginAccount.get
|
||||||
|
|
||||||
deleteWikiPage(repository.owner, repository.name, pageName, context.loginAccount.get.userName, s"Delete ${pageName}")
|
deleteWikiPage(repository.owner, repository.name, pageName, account.userName, account.mailAddress, s"Delete ${pageName}")
|
||||||
updateLastActivityDate(repository.owner, repository.name)
|
updateLastActivityDate(repository.owner, repository.name)
|
||||||
|
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import java.io.File
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import util.JGitUtil.DiffInfo
|
import util.{Directory, JGitUtil, LockUtil}
|
||||||
import util.{Directory, JGitUtil}
|
|
||||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
object WikiService {
|
object WikiService {
|
||||||
|
|
||||||
@@ -31,32 +28,39 @@ object WikiService {
|
|||||||
*/
|
*/
|
||||||
case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date)
|
case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date)
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* lock objects
|
// * lock objects
|
||||||
*/
|
// */
|
||||||
private val locks = new ConcurrentHashMap[String, AnyRef]()
|
// private val locks = new ConcurrentHashMap[String, Lock]()
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Returns the lock object for the specified repository.
|
// * Returns the lock object for the specified repository.
|
||||||
*/
|
// */
|
||||||
private def getLockObject(owner: String, repository: String): AnyRef = synchronized {
|
// private def getLockObject(owner: String, repository: String): Lock = synchronized {
|
||||||
val key = owner + "/" + repository
|
// val key = owner + "/" + repository
|
||||||
if(!locks.containsKey(key)){
|
// if(!locks.containsKey(key)){
|
||||||
locks.put(key, new AnyRef())
|
// locks.put(key, new ReentrantLock())
|
||||||
}
|
// }
|
||||||
locks.get(key)
|
// locks.get(key)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Synchronizes a given function which modifies the working copy of the wiki repository.
|
// * Synchronizes a given function which modifies the working copy of the wiki repository.
|
||||||
*
|
// *
|
||||||
* @param owner the repository owner
|
// * @param owner the repository owner
|
||||||
* @param repository the repository name
|
// * @param repository the repository name
|
||||||
* @param f the function which modifies the working copy of the wiki repository
|
// * @param f the function which modifies the working copy of the wiki repository
|
||||||
* @tparam T the return type of the given function
|
// * @tparam T the return type of the given function
|
||||||
* @return the result of the given function
|
// * @return the result of the given function
|
||||||
*/
|
// */
|
||||||
def lock[T](owner: String, repository: String)(f: => T): T = getLockObject(owner, repository).synchronized(f)
|
// def lock[T](owner: String, repository: String)(f: => T): T = {
|
||||||
|
// val lock = getLockObject(owner, repository)
|
||||||
|
// try {
|
||||||
|
// f
|
||||||
|
// } finally {
|
||||||
|
// lock.unlock()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +68,7 @@ trait WikiService {
|
|||||||
import WikiService._
|
import WikiService._
|
||||||
|
|
||||||
def createWikiRepository(loginAccount: model.Account, owner: String, repository: String): Unit = {
|
def createWikiRepository(loginAccount: model.Account, owner: String, repository: String): Unit = {
|
||||||
lock(owner, repository){
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
val dir = Directory.getWikiRepositoryDir(owner, repository)
|
val dir = Directory.getWikiRepositoryDir(owner, repository)
|
||||||
if(!dir.exists){
|
if(!dir.exists){
|
||||||
try {
|
try {
|
||||||
@@ -132,7 +136,7 @@ trait WikiService {
|
|||||||
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
|
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
|
||||||
content: String, committer: model.Account, message: String): Unit = {
|
content: String, committer: model.Account, message: String): Unit = {
|
||||||
|
|
||||||
lock(owner, repository){
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
// clone working copy
|
// clone working copy
|
||||||
val workDir = Directory.getWikiWorkDir(owner, repository)
|
val workDir = Directory.getWikiWorkDir(owner, repository)
|
||||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
cloneOrPullWorkingCopy(workDir, owner, repository)
|
||||||
@@ -168,8 +172,9 @@ trait WikiService {
|
|||||||
/**
|
/**
|
||||||
* Delete the wiki page.
|
* Delete the wiki page.
|
||||||
*/
|
*/
|
||||||
def deleteWikiPage(owner: String, repository: String, pageName: String, committer: String, message: String): Unit = {
|
def deleteWikiPage(owner: String, repository: String, pageName: String,
|
||||||
lock(owner, repository){
|
committer: String, mailAddress: String, message: String): Unit = {
|
||||||
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
// clone working copy
|
// clone working copy
|
||||||
val workDir = Directory.getWikiWorkDir(owner, repository)
|
val workDir = Directory.getWikiWorkDir(owner, repository)
|
||||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
cloneOrPullWorkingCopy(workDir, owner, repository)
|
||||||
@@ -181,8 +186,7 @@ trait WikiService {
|
|||||||
git.rm.addFilepattern(pageName + ".md").call
|
git.rm.addFilepattern(pageName + ".md").call
|
||||||
|
|
||||||
// commit and push
|
// commit and push
|
||||||
// TODO committer's mail address
|
git.commit.setAuthor(committer, mailAddress).setMessage(message).call
|
||||||
git.commit.setAuthor(committer, committer + "@devnull").setMessage(message).call
|
|
||||||
git.push.call
|
git.push.call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/main/scala/util/LockUtil.scala
Normal file
36
src/main/scala/util/LockUtil.scala
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.locks.{ReentrantLock, Lock}
|
||||||
|
|
||||||
|
object LockUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lock objects
|
||||||
|
*/
|
||||||
|
private val locks = new ConcurrentHashMap[String, Lock]()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the lock object for the specified repository.
|
||||||
|
*/
|
||||||
|
private def getLockObject(key: String): Lock = synchronized {
|
||||||
|
if(!locks.containsKey(key)){
|
||||||
|
locks.put(key, new ReentrantLock())
|
||||||
|
}
|
||||||
|
locks.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes a given function which modifies the working copy of the wiki repository.
|
||||||
|
*/
|
||||||
|
def lock[T](key: String)(f: => T): T = {
|
||||||
|
val lock = getLockObject(key)
|
||||||
|
try {
|
||||||
|
lock.lock()
|
||||||
|
f
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user