Skip to content

Commit dd36123

Browse files
committed
Merge branch 'cwltool' into ro-workflow-description
# Conflicts: # src/main/java/org/commonwl/view/researchobject/ROBundleService.java # src/test/java/org/commonwl/view/researchobject/ROBundleServiceTest.java
2 parents f68617b + df8fb7f commit dd36123

File tree

10 files changed

+240
-71
lines changed

10 files changed

+240
-71
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
xFROM maven:3.5-jdk-8-alpine
1+
FROM maven:3.5-jdk-8-alpine
22
MAINTAINER Stian Soiland-Reyes <[email protected]>
33

44
# Build-time metadata as defined at http://label-schema.org

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Developers and [contributors](https://github.com/common-workflow-language/cwlvie
135135
* **Mark Robinson** http://orcid.org/0000-0002-8184-7507
136136
* Stian Soiland-Reyes http://orcid.org/0000-0001-9842-9718
137137
* Michael Crusoe http://orcid.org/0000-0002-2961-9670
138+
* Carole Goble http://orcid.org/0000-0003-1219-2137
138139

139140
Thanks to:
140141

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.commonwl.view.git;
21+
22+
import org.springframework.stereotype.Component;
23+
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
27+
/**
28+
* Object to manage concurrent access to Git repositories
29+
*/
30+
@Component
31+
public class GitSemaphore {
32+
33+
private static Map<String, Integer> currentRepos = new HashMap<>();
34+
35+
/**
36+
* Note that a thread will be accessing the repository
37+
* @param repoUrl The url of the repository
38+
* @return Whether the resource can be accessed safely
39+
* (no other threads are using it)
40+
*/
41+
public synchronized boolean acquire(String repoUrl) {
42+
if (currentRepos.containsKey(repoUrl)) {
43+
currentRepos.put(repoUrl, currentRepos.get(repoUrl) + 1);
44+
return false;
45+
} else {
46+
currentRepos.put(repoUrl, 1);
47+
return true;
48+
}
49+
}
50+
51+
/**
52+
* Release use of the shared resource
53+
* @param repoUrl The url of the repository
54+
*/
55+
public synchronized void release(String repoUrl) {
56+
if (currentRepos.containsKey(repoUrl)) {
57+
int threadCountUsing = currentRepos.get(repoUrl);
58+
if (threadCountUsing > 1) {
59+
currentRepos.put(repoUrl, threadCountUsing - 1);
60+
} else {
61+
currentRepos.remove(repoUrl);
62+
}
63+
}
64+
}
65+
66+
}

src/main/java/org/commonwl/view/git/GitService.java

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import java.util.HashSet;
4040
import java.util.Set;
4141

42+
import static org.apache.jena.ext.com.google.common.io.Files.createTempDir;
43+
4244
/**
4345
* Handles Git related functionality
4446
*/
@@ -63,31 +65,43 @@ public GitService(@Value("${gitStorage}") Path gitStorage,
6365
/**
6466
* Gets a repository, cloning into a local directory or
6567
* @param gitDetails The details of the Git repository
68+
* @param reuseDir Whether the cached repository can be used
6669
* @returns The git object for the repository
6770
*/
68-
public Git getRepository(GitDetails gitDetails)
71+
public Git getRepository(GitDetails gitDetails, boolean reuseDir)
6972
throws GitAPIException {
7073
Git repo = null;
7174
try {
72-
// Base dir from configuration, name from hash of repository URL
73-
File baseDir = new File(gitStorage.toString());
74-
String baseName = DigestUtils.shaHex(GitDetails.normaliseUrl(gitDetails.getRepoUrl()));
75+
if (reuseDir) {
76+
// Base dir from configuration, name from hash of repository URL
77+
File baseDir = new File(gitStorage.toString());
78+
String baseName = DigestUtils.shaHex(GitDetails.normaliseUrl(gitDetails.getRepoUrl()));
7579

76-
// Check if folder already exists
77-
File repoDir = new File(baseDir, baseName);
78-
if (repoDir.exists() && repoDir.isDirectory()) {
80+
// Check if folder already exists
81+
File repoDir = new File(baseDir, baseName);
82+
if (repoDir.exists() && repoDir.isDirectory()) {
7983
repo = Git.open(repoDir);
8084
repo.fetch().call();
81-
} else {
82-
// Create a folder and clone repository into it
83-
if (repoDir.mkdir()) {
84-
repo = Git.cloneRepository()
85-
.setCloneSubmodules(cloneSubmodules)
86-
.setURI(gitDetails.getRepoUrl())
87-
.setDirectory(repoDir)
88-
.setCloneAllBranches(true)
89-
.call();
85+
} else {
86+
// Create a folder and clone repository into it
87+
if (repoDir.mkdir()) {
88+
repo = Git.cloneRepository()
89+
.setCloneSubmodules(cloneSubmodules)
90+
.setURI(gitDetails.getRepoUrl())
91+
.setDirectory(repoDir)
92+
.setCloneAllBranches(true)
93+
.call();
94+
}
9095
}
96+
} else {
97+
// Another thread is already using the existing folder
98+
// Must create another temporary one
99+
repo = Git.cloneRepository()
100+
.setCloneSubmodules(cloneSubmodules)
101+
.setURI(gitDetails.getRepoUrl())
102+
.setDirectory(createTempDir())
103+
.setCloneAllBranches(true)
104+
.call();
91105
}
92106

93107
// Checkout the specific branch or commit ID

src/main/java/org/commonwl/view/researchobject/ROBundleService.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.commonwl.view.cwl.CWLValidationException;
3131
import org.commonwl.view.cwl.RDFService;
3232
import org.commonwl.view.git.GitDetails;
33+
import org.commonwl.view.git.GitSemaphore;
3334
import org.commonwl.view.git.GitService;
3435
import org.commonwl.view.git.GitType;
3536
import org.commonwl.view.graphviz.GraphVizService;
@@ -71,6 +72,7 @@ public class ROBundleService {
7172
private GitService gitService;
7273
private RDFService rdfService;
7374
private CWLTool cwlTool;
75+
private GitSemaphore gitSemaphore;
7476

7577
// Configuration variables
7678
private Agent appAgent;
@@ -97,6 +99,7 @@ public ROBundleService(@Value("${bundleStorage}") Path bundleStorage,
9799
GraphVizService graphVizService,
98100
GitService gitService,
99101
RDFService rdfService,
102+
GitSemaphore gitSemaphore,
100103
CWLTool cwlTool) throws URISyntaxException {
101104
this.bundleStorage = bundleStorage;
102105
this.appAgent = new Agent(appName);
@@ -105,6 +108,7 @@ public ROBundleService(@Value("${bundleStorage}") Path bundleStorage,
105108
this.graphVizService = graphVizService;
106109
this.gitService = gitService;
107110
this.rdfService = rdfService;
111+
this.gitSemaphore = gitSemaphore;
108112
this.cwlTool = cwlTool;
109113
}
110114

@@ -137,10 +141,16 @@ public Bundle createBundle(Workflow workflow, GitDetails gitInfo) throws IOExcep
137141

138142
// Add the files from the repo to this workflow
139143
Set<HashableAgent> authors = new HashSet<>();
140-
Git gitRepo = gitService.getRepository(workflow.getRetrievedFrom());
141-
Path relativePath = Paths.get(FilenameUtils.getPath(gitInfo.getPath()));
142-
Path gitPath = gitRepo.getRepository().getWorkTree().toPath().resolve(relativePath);
143-
addFilesToBundle(gitInfo, bundle, bundlePath, gitRepo, gitPath, authors, workflow);
144+
145+
boolean safeToAccess = gitSemaphore.acquire(gitInfo.getRepoUrl());
146+
try {
147+
Git gitRepo = gitService.getRepository(workflow.getRetrievedFrom(), safeToAccess);
148+
Path relativePath = Paths.get(FilenameUtils.getPath(gitInfo.getPath()));
149+
Path gitPath = gitRepo.getRepository().getWorkTree().toPath().resolve(relativePath);
150+
addFilesToBundle(gitInfo, bundle, bundlePath, gitRepo, gitPath, authors, workflow);
151+
} finally {
152+
gitSemaphore.release(gitInfo.getRepoUrl());
153+
}
144154

145155
// Add combined authors
146156
manifest.setAuthoredBy(new ArrayList<>(authors));
@@ -181,7 +191,6 @@ public Bundle createBundle(Workflow workflow, GitDetails gitInfo) throws IOExcep
181191
try {
182192
addAggregation(bundle, manifestAnnotations,
183193
"workflow.ttl", cwlTool.getRDF(rawUrl));
184-
185194
} catch (CWLValidationException ex) {
186195
logger.error("Could not get RDF for workflow from raw URL", ex.getMessage());
187196
}

src/main/java/org/commonwl/view/workflow/WorkflowService.java

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.commonwl.view.cwl.CWLToolRunner;
2424
import org.commonwl.view.cwl.CWLToolStatus;
2525
import org.commonwl.view.git.GitDetails;
26+
import org.commonwl.view.git.GitSemaphore;
2627
import org.commonwl.view.git.GitService;
2728
import org.commonwl.view.graphviz.GraphVizService;
2829
import org.commonwl.view.researchobject.ROBundleFactory;
@@ -56,6 +57,7 @@ public class WorkflowService {
5657
private final ROBundleFactory ROBundleFactory;
5758
private final GraphVizService graphVizService;
5859
private final CWLToolRunner cwlToolRunner;
60+
private final GitSemaphore gitSemaphore;
5961
private final int cacheDays;
6062

6163
@Autowired
@@ -66,6 +68,7 @@ public WorkflowService(GitService gitService,
6668
ROBundleFactory ROBundleFactory,
6769
GraphVizService graphVizService,
6870
CWLToolRunner cwlToolRunner,
71+
GitSemaphore gitSemaphore,
6972
@Value("${cacheDays}") int cacheDays) {
7073
this.gitService = gitService;
7174
this.cwlService = cwlService;
@@ -75,6 +78,7 @@ public WorkflowService(GitService gitService,
7578
this.graphVizService = graphVizService;
7679
this.cwlToolRunner = cwlToolRunner;
7780
this.cacheDays = cacheDays;
81+
this.gitSemaphore = gitSemaphore;
7882
}
7983

8084
/**
@@ -217,50 +221,58 @@ public File getROBundle(GitDetails gitDetails) throws ROBundleNotFoundException
217221
*/
218222
public QueuedWorkflow createQueuedWorkflow(GitDetails gitInfo)
219223
throws GitAPIException, WorkflowNotFoundException, IOException {
224+
QueuedWorkflow queuedWorkflow;
220225

221-
// Clone repository to temporary folder
222-
Git repo = null;
223-
while (repo == null) {
224-
try {
225-
repo = gitService.getRepository(gitInfo);
226-
} catch (RefNotFoundException ex) {
227-
// Attempt slashes in branch fix
228-
GitDetails correctedForSlash = transferPathToBranch(gitInfo);
229-
if (correctedForSlash != null) {
230-
gitInfo = correctedForSlash;
231-
} else {
232-
throw ex;
226+
try {
227+
// Clone repository to temporary folder
228+
Git repo = null;
229+
while (repo == null) {
230+
try {
231+
boolean safeToAccess = gitSemaphore.acquire(gitInfo.getRepoUrl());
232+
repo = gitService.getRepository(gitInfo, safeToAccess);
233+
} catch (RefNotFoundException ex) {
234+
// Attempt slashes in branch fix
235+
GitDetails correctedForSlash = transferPathToBranch(gitInfo);
236+
if (correctedForSlash != null) {
237+
gitSemaphore.release(gitInfo.getRepoUrl());
238+
gitInfo = correctedForSlash;
239+
} else {
240+
throw ex;
241+
}
233242
}
234243
}
235-
}
236-
File localPath = repo.getRepository().getWorkTree();
237-
String latestCommit = gitService.getCurrentCommitID(repo);
244+
File localPath = repo.getRepository().getWorkTree();
245+
String latestCommit = gitService.getCurrentCommitID(repo);
238246

239-
Path pathToWorkflowFile = localPath.toPath().resolve(gitInfo.getPath()).normalize().toAbsolutePath();
240-
// Prevent path traversal attacks
241-
if (!pathToWorkflowFile.startsWith(localPath.toPath().normalize().toAbsolutePath())) {
242-
throw new WorkflowNotFoundException();
243-
}
247+
Path pathToWorkflowFile = localPath.toPath().resolve(gitInfo.getPath()).normalize().toAbsolutePath();
248+
// Prevent path traversal attacks
249+
if (!pathToWorkflowFile.startsWith(localPath.toPath().normalize().toAbsolutePath())) {
250+
throw new WorkflowNotFoundException();
251+
}
244252

245-
File workflowFile = new File(pathToWorkflowFile.toString());
246-
Workflow basicModel = cwlService.parseWorkflowNative(workflowFile);
253+
File workflowFile = new File(pathToWorkflowFile.toString());
254+
Workflow basicModel = cwlService.parseWorkflowNative(workflowFile);
247255

248-
// Set origin details
249-
basicModel.setRetrievedOn(new Date());
250-
basicModel.setRetrievedFrom(gitInfo);
251-
basicModel.setLastCommit(latestCommit);
256+
// Set origin details
257+
basicModel.setRetrievedOn(new Date());
258+
basicModel.setRetrievedFrom(gitInfo);
259+
basicModel.setLastCommit(latestCommit);
252260

253-
// Save the queued workflow to database
254-
QueuedWorkflow queuedWorkflow = new QueuedWorkflow();
255-
queuedWorkflow.setTempRepresentation(basicModel);
256-
queuedWorkflowRepository.save(queuedWorkflow);
261+
// Save the queued workflow to database
262+
queuedWorkflow = new QueuedWorkflow();
263+
queuedWorkflow.setTempRepresentation(basicModel);
264+
queuedWorkflowRepository.save(queuedWorkflow);
257265

258-
// ASYNC OPERATIONS
259-
// Parse with cwltool and update model
260-
try {
261-
cwlToolRunner.createWorkflowFromQueued(queuedWorkflow, workflowFile);
262-
} catch (Exception e) {
263-
logger.error("Could not update workflow with cwltool", e);
266+
// ASYNC OPERATIONS
267+
// Parse with cwltool and update model
268+
try {
269+
cwlToolRunner.createWorkflowFromQueued(queuedWorkflow, workflowFile);
270+
} catch (Exception e) {
271+
logger.error("Could not update workflow with cwltool", e);
272+
}
273+
274+
} finally {
275+
gitSemaphore.release(gitInfo.getRepoUrl());
264276
}
265277

266278
// Return this model to be displayed
@@ -275,14 +287,17 @@ public void retryCwltool(QueuedWorkflow queuedWorkflow) {
275287
queuedWorkflow.setMessage(null);
276288
queuedWorkflow.setCwltoolStatus(CWLToolStatus.RUNNING);
277289
queuedWorkflowRepository.save(queuedWorkflow);
290+
GitDetails gitDetails = queuedWorkflow.getTempRepresentation().getRetrievedFrom();
291+
boolean safeToAccess = gitSemaphore.acquire(gitDetails.getRepoUrl());
278292
try {
279-
GitDetails gitDetails = queuedWorkflow.getTempRepresentation().getRetrievedFrom();
280-
Git repo = gitService.getRepository(gitDetails);
293+
Git repo = gitService.getRepository(gitDetails, safeToAccess);
281294
File localPath = repo.getRepository().getWorkTree();
282295
Path pathToWorkflowFile = localPath.toPath().resolve(gitDetails.getPath()).normalize().toAbsolutePath();
283296
cwlToolRunner.createWorkflowFromQueued(queuedWorkflow, new File(pathToWorkflowFile.toString()));
284297
} catch (Exception e) {
285298
logger.error("Could not update workflow with cwltool", e);
299+
} finally {
300+
gitSemaphore.release(gitDetails.getRepoUrl());
286301
}
287302
}
288303

@@ -343,8 +358,14 @@ private boolean cacheExpired(Workflow workflow) {
343358
// Cache expiry time has elapsed
344359
// Check current head of the branch with the cached head
345360
logger.info("Time has expired for caching, checking commits...");
346-
Git repo = gitService.getRepository(workflow.getRetrievedFrom());
347-
String currentHead = gitService.getCurrentCommitID(repo);
361+
String currentHead;
362+
boolean safeToAccess = gitSemaphore.acquire(workflow.getRetrievedFrom().getRepoUrl());
363+
try {
364+
Git repo = gitService.getRepository(workflow.getRetrievedFrom(), safeToAccess);
365+
currentHead = gitService.getCurrentCommitID(repo);
366+
} finally {
367+
gitSemaphore.release(workflow.getRetrievedFrom().getRepoUrl());
368+
}
348369
logger.info("Current: " + workflow.getLastCommit() + ", HEAD: " + currentHead);
349370

350371
// Reset date in database if there are still no changes

src/main/resources/static/css/main-20170616.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ body {
262262
.legend .defaults { background-color: #D5AEFC; }
263263
.legend .selectednode { background-color: #B9FFBB; }
264264

265+
.recommendation {
266+
font-weight: bold;
267+
}
268+
265269
tr.selected, .table-hover tbody tr.selected:hover td, .table-hover tbody tr.selected:hover th {
266270
background-color: #b9ffbb !important;
267271
}

0 commit comments

Comments
 (0)