diff --git a/src/main/html/webapp/components/core/job/detail/results/share-folder.stache b/src/main/html/webapp/components/core/job/detail/results/share-folder.stache index 4786e40f..9ade7201 100644 --- a/src/main/html/webapp/components/core/job/detail/results/share-folder.stache +++ b/src/main/html/webapp/components/core/job/detail/results/share-folder.stache @@ -18,7 +18,7 @@ {{/files}}
Use the following command to download all results at once:
- +
diff --git a/src/main/html/webapp/helpers/helpers.js b/src/main/html/webapp/helpers/helpers.js index e8fbdf49..c7cf0ed7 100644 --- a/src/main/html/webapp/helpers/helpers.js +++ b/src/main/html/webapp/helpers/helpers.js @@ -40,7 +40,7 @@ function renderTreeItem(jobId, items, level) { html += renderTreeItem(jobId, items[i].childs, level + 1); } else { html += ' ' - html += '' + items[i].name + ''; + html += '' + items[i].name + ''; html += '    (' + items[i].size + ")"; } html += ""; diff --git a/src/main/java/cloudgene/mapred/CommandLineInterface.java b/src/main/java/cloudgene/mapred/CommandLineInterface.java index e2e87a11..0a63dc0c 100644 --- a/src/main/java/cloudgene/mapred/CommandLineInterface.java +++ b/src/main/java/cloudgene/mapred/CommandLineInterface.java @@ -23,7 +23,7 @@ private void printHeader() { System.out.println(); System.out.println("Cloudgene " + Application.VERSION); System.out.println("http://www.cloudgene.io"); - System.out.println("(c) 2009-2032 Lukas Forer and Sebastian Schoenherr"); + System.out.println("(c) 2009-2024 Lukas Forer and Sebastian Schoenherr"); System.out.println(); } diff --git a/src/main/java/cloudgene/mapred/database/DownloadDao.java b/src/main/java/cloudgene/mapred/database/DownloadDao.java index c3256763..987cc62d 100644 --- a/src/main/java/cloudgene/mapred/database/DownloadDao.java +++ b/src/main/java/cloudgene/mapred/database/DownloadDao.java @@ -155,7 +155,35 @@ public Download findByJobAndPath(String job, String path) { } } - class DownloadMapper implements IRowMapper { + public Download findByParameterAndName(CloudgeneParameterOutput param, String filename) { + StringBuilder sql = new StringBuilder(); + sql.append("select * "); + sql.append("from downloads "); + sql.append("where name = ? and parameter_id = ? "); + sql.append("order by path "); + + Object[] params = new Object[2]; + params[0] = filename; + params[1] = param.getId(); + + Download result = null; + + try { + + result = (Download) queryForObject(sql.toString(), params, + new DownloadMapper()); + + log.debug("find download by param " + param.getId() + " and path " + filename + + " successful. results: " + result); + + return result; + } catch (SQLException e) { + log.error("find download by job and path failed.", e); + return null; + } + } + + class DownloadMapper implements IRowMapper { @Override public Object mapRow(ResultSet rs, int row) throws SQLException { diff --git a/src/main/java/cloudgene/mapred/database/ParameterDao.java b/src/main/java/cloudgene/mapred/database/ParameterDao.java index b33d7718..42e29eff 100644 --- a/src/main/java/cloudgene/mapred/database/ParameterDao.java +++ b/src/main/java/cloudgene/mapred/database/ParameterDao.java @@ -28,12 +28,12 @@ public ParameterDao(Database database) { public boolean insert(CloudgeneParameterInput parameter) { StringBuilder sql = new StringBuilder(); - sql.append("insert into parameter (name, `value`, input, job_id, type, variable, download, format, admin_only) "); - sql.append("values (?,?,?,?,?,?,?,?,?)"); + sql.append("insert into parameter (name, `value`, input, job_id, type, variable, download, format, admin_only, hash) "); + sql.append("values (?,?,?,?,?,?,?,?,?,?)"); try { - Object[] params = new Object[9]; + Object[] params = new Object[10]; if (parameter.getDescription() != null) { params[0] = parameter.getDescription().substring(0, Math.min(parameter.getDescription().length(), 100)); } else { @@ -47,6 +47,7 @@ public boolean insert(CloudgeneParameterInput parameter) { params[6] = false; params[7] = ""; params[8] = parameter.isAdminOnly(); + params[9] = parameter.getHash(); int paramId = insert(sql.toString(), params); parameter.setId(paramId); @@ -63,12 +64,12 @@ public boolean insert(CloudgeneParameterInput parameter) { public boolean insert(CloudgeneParameterOutput parameter) { StringBuilder sql = new StringBuilder(); - sql.append("insert into parameter (name, `value`, input, job_id, type, variable, download, format, admin_only) "); - sql.append("values (?,?,?,?,?,?,?,?,?)"); + sql.append("insert into parameter (name, `value`, input, job_id, type, variable, download, format, admin_only, hash) "); + sql.append("values (?,?,?,?,?,?,?,?,?,?)"); try { - Object[] params = new Object[9]; + Object[] params = new Object[10]; params[0] = parameter.getDescription().substring(0, Math.min(parameter.getDescription().length(), 100)); params[1] = parameter.getValue(); params[2] = false; @@ -78,6 +79,7 @@ public boolean insert(CloudgeneParameterOutput parameter) { params[6] = parameter.isDownload(); params[7] = ""; params[8] = parameter.isAdminOnly(); + params[9] = parameter.getHash(); int paramId = insert(sql.toString(), params); parameter.setId(paramId); @@ -180,6 +182,61 @@ public CloudgeneParameterOutput findById(int id) { } } + public CloudgeneParameterOutput findByHash(String hash) { + StringBuilder sql = new StringBuilder(); + sql.append("select * "); + sql.append("from parameter "); + sql.append("where hash = ?"); + + Object[] params = new Object[1]; + params[0] = hash; + + CloudgeneParameterOutput result = null; + + try { + + result = (CloudgeneParameterOutput) queryForObject(sql.toString(), params, new ParameterOutputMapper()); + + DownloadDao downloadDao = new DownloadDao(database); + List downloads = downloadDao.findAllByParameter(result); + result.setFiles(downloads); + + log.debug("find parameter by hash '" + hash + "' successful."); + + return result; + } catch (SQLException e) { + log.error("find parameter by hash '" + hash + "' failed.", e); + return null; + } + } + + public List findAllOutput() { + StringBuilder sql = new StringBuilder(); + sql.append("select * "); + sql.append("from parameter "); + sql.append("where input = false"); + + List result = new Vector(); + + try { + + result = query(sql.toString(), new ParameterOutputMapper()); + + DownloadDao downloadDao = new DownloadDao(database); + for (CloudgeneParameterOutput parameter : result) { + List downloads = downloadDao.findAllByParameter(parameter); + parameter.setFiles(downloads); + } + + log.debug("find all output parameters successful. results: " + result.size()); + + return result; + } catch (SQLException e) { + log.error("find all output parameters failed.", e); + return null; + } + } + class ParameterInputMapper implements IRowMapper { @Override @@ -192,6 +249,7 @@ public Object mapRow(ResultSet rs, int row) throws SQLException { parameter.setType(WdlParameterInputType.getEnum(rs.getString("type"))); parameter.setId(rs.getInt("id")); parameter.setAdminOnly(rs.getBoolean("admin_only")); + parameter.setHash(rs.getString("hash")); return parameter; } @@ -211,6 +269,7 @@ public Object mapRow(ResultSet rs, int row) throws SQLException { parameter.setDownload(rs.getBoolean("download")); parameter.setId(rs.getInt("id")); parameter.setAdminOnly(rs.getBoolean("admin_only")); + parameter.setHash(rs.getString("hash")); return parameter; } diff --git a/src/main/java/cloudgene/mapred/jobs/CloudgeneJob.java b/src/main/java/cloudgene/mapred/jobs/CloudgeneJob.java index 96f14f4d..43f3e1fb 100644 --- a/src/main/java/cloudgene/mapred/jobs/CloudgeneJob.java +++ b/src/main/java/cloudgene/mapred/jobs/CloudgeneJob.java @@ -59,6 +59,7 @@ public CloudgeneJob(User user, String id, WdlApp app, Map params outputParams = new Vector(); for (WdlParameterOutput output : app.getWorkflow().getOutputs()) { CloudgeneParameterOutput newOutput = new CloudgeneParameterOutput(output); + newOutput.initHash(); newOutput.setJob(this); outputParams.add(newOutput); } @@ -93,6 +94,7 @@ protected void initLogOutput() { logOutput.setName(CLOUDGENE_LOGS_PARAM); logOutput.setType(WdlParameterOutputType.LOCAL_FOLDER); logOutput.setJob(this); + logOutput.initHash(); } @Override diff --git a/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterInput.java b/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterInput.java index d06e458d..09bb16cd 100644 --- a/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterInput.java +++ b/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterInput.java @@ -21,6 +21,8 @@ public class CloudgeneParameterInput { private boolean adminOnly = false; + private String hash = ""; + public CloudgeneParameterInput() { } @@ -97,4 +99,11 @@ public boolean isAdminOnly() { return adminOnly; } + public void setHash(String hash) { + this.hash = hash; + } + + public String getHash() { + return hash; + } } diff --git a/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterOutput.java b/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterOutput.java index 6f150967..944ffbfe 100644 --- a/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterOutput.java +++ b/src/main/java/cloudgene/mapred/jobs/CloudgeneParameterOutput.java @@ -1,11 +1,13 @@ package cloudgene.mapred.jobs; import java.util.List; +import java.util.Random; import java.util.Vector; import cloudgene.mapred.util.HashUtil; import cloudgene.mapred.wdl.WdlParameterOutput; import cloudgene.mapred.wdl.WdlParameterOutputType; +import org.apache.commons.math3.random.RandomGenerator; public class CloudgeneParameterOutput { @@ -141,13 +143,17 @@ public void setHash(String hash) { public String getHash() { return hash; } - - public String createHash() { - String hash = ""; - for (Download download: files) { - hash += download.getHash(); + + public void initHash() { + String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + hash = ""; + Random random = new Random(); + int length = 40; + for (int i = 0; i < length; i++) { + int index = random.nextInt(alphabet.length()); + char randomChar = alphabet.charAt(index); + hash += randomChar; } - return HashUtil.getSha256(hash); } } diff --git a/src/main/java/cloudgene/mapred/server/Application.java b/src/main/java/cloudgene/mapred/server/Application.java index 93472179..ba18099c 100644 --- a/src/main/java/cloudgene/mapred/server/Application.java +++ b/src/main/java/cloudgene/mapred/server/Application.java @@ -27,7 +27,7 @@ @Context public class Application { - public static final String VERSION = "3.0.0-beta4"; + public static final String VERSION = "3.0.0-beta5"; private Database database; diff --git a/src/main/java/cloudgene/mapred/server/controller/DownloadController.java b/src/main/java/cloudgene/mapred/server/controller/DownloadController.java index 8f8cfd1e..47fd8c20 100644 --- a/src/main/java/cloudgene/mapred/server/controller/DownloadController.java +++ b/src/main/java/cloudgene/mapred/server/controller/DownloadController.java @@ -82,26 +82,36 @@ public MutableHttpResponse downloadPublicLink(String hash, String f } - @Get("/get/{paramId}/{hash}") + @Get("/browse/{hash}/{filename:.+}") @Secured(SecurityRule.IS_ANONYMOUS) - public String downloadScript(String paramId, String hash) { - - int id = -1; - try { - id = Integer.parseInt(paramId); - } catch (NumberFormatException e) { - throw new JsonHttpStatusException(HttpStatus.BAD_REQUEST, "Parameter ID is not numeric."); - } + public MutableHttpResponse downloadByParamHash(String hash, String filename) + throws URISyntaxException, IOException { ParameterDao parameterDao = new ParameterDao(application.getDatabase()); - CloudgeneParameterOutput param = parameterDao.findById(id); + CloudgeneParameterOutput param = parameterDao.findByHash(hash); if (param == null) { - throw new JsonHttpStatusException(HttpStatus.NOT_FOUND, "Param " + param + " not found."); + throw new JsonHttpStatusException(HttpStatus.NOT_FOUND, "Param for hash " + hash + " not found."); } - if (!hash.equals(param.createHash())) { - throw new JsonHttpStatusException(HttpStatus.FORBIDDEN, "Download forbidden."); + DownloadDao dao = new DownloadDao(application.getDatabase()); + Download download = dao.findByParameterAndName(param, filename); + + String message = String.format("Job: Anonymously downloading file '%s' (hash %s)", filename, hash); + log.info(message); + return downloadService.download(download); + + } + + @Get("/get/{hash}") + @Secured(SecurityRule.IS_ANONYMOUS) + public String downloadScript(String hash) { + + ParameterDao parameterDao = new ParameterDao(application.getDatabase()); + CloudgeneParameterOutput param = parameterDao.findByHash(hash); + + if (param == null) { + throw new JsonHttpStatusException(HttpStatus.NOT_FOUND, "Param for hash " + hash + " not found."); } DownloadDao dao = new DownloadDao(application.getDatabase()); diff --git a/src/main/java/cloudgene/mapred/server/responses/JobResponse.java b/src/main/java/cloudgene/mapred/server/responses/JobResponse.java index e53eb2f9..dfcdd94a 100644 --- a/src/main/java/cloudgene/mapred/server/responses/JobResponse.java +++ b/src/main/java/cloudgene/mapred/server/responses/JobResponse.java @@ -169,9 +169,7 @@ public static JobResponse build(AbstractJob job, User user) { // create tree for (CloudgeneParameterOutput param : job.getOutputParams()) { - String hash = param.createHash(); - param.setHash(hash); - param.setTree(JobResultsTreeUtil.createTree(param.getFiles())); + param.setTree(JobResultsTreeUtil.createTree(param)); } // removes outputs that are for admin only diff --git a/src/main/java/cloudgene/mapred/util/JobResultsTreeUtil.java b/src/main/java/cloudgene/mapred/util/JobResultsTreeUtil.java index 09f240df..bd45da04 100644 --- a/src/main/java/cloudgene/mapred/util/JobResultsTreeUtil.java +++ b/src/main/java/cloudgene/mapred/util/JobResultsTreeUtil.java @@ -4,14 +4,15 @@ import java.util.List; import java.util.Vector; +import cloudgene.mapred.jobs.CloudgeneParameterOutput; import cloudgene.mapred.jobs.Download; import cloudgene.mapred.jobs.JobResultsTreeItem; public class JobResultsTreeUtil { - public static List createTree(List files) { + public static List createTree(CloudgeneParameterOutput param) { List items = new Vector(); - for (Download file : files) { + for (Download file : param.getFiles()) { String[] tiles = file.getName().split("/"); JobResultsTreeItem root = null; for (int i = 0; i < tiles.length - 1; i++) { @@ -32,7 +33,11 @@ public static List createTree(List files) { } JobResultsTreeItem item = new JobResultsTreeItem(); item.setName(tiles[tiles.length - 1]); - item.setPath(file.getPath()); + if (param.getHash() != null) { + item.setPath("/browse/" + param.getHash() + "/" + file.getName()); + } else { + item.setPath("/share/results/" + file.getHash() + "/" + file.getName()); + } item.setHash(file.getHash()); item.setSize(file.getSize()); item.setFolder(false); diff --git a/src/main/sql/updates.sql b/src/main/sql/updates.sql index f4849220..44d54554 100644 --- a/src/main/sql/updates.sql +++ b/src/main/sql/updates.sql @@ -168,3 +168,6 @@ alter table job add column user_agent VARCHAR (400); -- 3.0.0-beta1 alter table `user` add column api_token_expires_on timestamp null default null; + +-- 3.0.0-beta5 +alter table `parameter` add column hash varchar(300) null default null;