/*
* Copyright (C) 2011 SeqWare
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.seqware.webservice.resources.tables;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sf.beanlib.CollectionPropertyName;
import net.sf.beanlib.hibernate3.Hibernate3DtoCopier;
import net.sourceforge.seqware.common.business.FileService;
import net.sourceforge.seqware.common.business.WorkflowRunService;
import net.sourceforge.seqware.common.factory.BeanFactory;
import net.sourceforge.seqware.common.factory.DBAccess;
import net.sourceforge.seqware.common.metadata.MetadataDB;
import net.sourceforge.seqware.common.model.File;
import net.sourceforge.seqware.common.model.IUS;
import net.sourceforge.seqware.common.model.Lane;
import net.sourceforge.seqware.common.model.Processing;
import net.sourceforge.seqware.common.model.WorkflowRun;
import net.sourceforge.seqware.common.model.lists.WorkflowRunList2;
import net.sourceforge.seqware.common.util.Log;
import net.sourceforge.seqware.common.util.xmltools.JaxbObject;
import net.sourceforge.seqware.common.util.xmltools.XmlTools;
import static net.sourceforge.seqware.webservice.resources.BasicResource.testIfNull;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.lang3.ArrayUtils;
import org.restlet.data.Status;
import org.restlet.resource.Get;
import org.restlet.resource.ResourceException;
import org.w3c.dom.Document;
/**
* This resource will pull back the workflow runs that are generated from a particular file.
*
* In order: a) Workflow runs found via processing, processing_relationship, workflow_run b) Workflow runs found via the IUS,
* ius_workflow_runs c) Workflow runs found via the lane, lane_workflow_runs
*
* @author dyuen
* @version $Id: $Id
*/
public class FileChildWorkflowRunsResource extends DatabaseResource {
/**
* A direct search uses the workflow_run_input_files table rather than attempting a search via the ius_workflow_run and
* lane_workflow_run and processing hierarchy
*/
public static final String DIRECT_SEARCH = "DIRECT_SEARCH";
/**
* <p>
* Constructor for FileChildWorkflowRunsResource.
* </p>
*/
public FileChildWorkflowRunsResource() {
super("file");
}
/**
* <p>
* getXml.
* </p>
*
* @throws java.sql.SQLException
*/
@Get
public void getXml() throws SQLException {
authenticate();
final Hibernate3DtoCopier copier = new Hibernate3DtoCopier();
JaxbObject jaxbTool;
Log.debug("Dealing with FileChildWorkflowRunsResource");
if (queryValues.keySet().contains(DIRECT_SEARCH) && queryValues.get(DIRECT_SEARCH).equalsIgnoreCase("true")) {
handleDirectGetXML();
return;
}
FileService fs = BeanFactory.getFileServiceBean();
Set<File> files = new HashSet<>();
SEARCH_TYPE searchType = SEARCH_TYPE.CHILDREN_VIA_PROCESSING_RELATIONSHIP;
Log.debug("File service started");
for (String key : queryValues.keySet()) {
Log.debug("key: " + key + " -> " + queryValues.get(key));
if (key.equals("files")) {
String value = queryValues.get(key);
String[] filesSWIDs = value.split(",");
for (String swid : filesSWIDs) {
File findByID = (File) testIfNull(fs.findBySWAccession(Integer.valueOf(swid)));
files.add(findByID);
}
}
if (key.equals("search")) {
String value = queryValues.get(key);
try {
searchType = SEARCH_TYPE.valueOf(value);
} catch (IllegalArgumentException e) {
throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "Object cannot be found");
}
}
}
Log.debug("Working with " + files.size() + " files and doing a search of type: " + searchType.toString());
// these variables will be used to return information
jaxbTool = new JaxbObject<>();
WorkflowRunList2 eList = new WorkflowRunList2();
eList.setList(new ArrayList());
Log.debug("JaxbObjects started");
assert eList.getList().isEmpty();
// the logic here is, we consider first workflow_run children found via the children of the processing
// if that is empty, then we consider workflow_run found via the IUS (ius_workflow_run table)
// if that is empty, then we consider workflow_run found via the lane (lane_workflow_run table)
if (searchType == SEARCH_TYPE.CHILDREN_VIA_PROCESSING_RELATIONSHIP) {
eList = handleWorkflowRunsViaChild(files, copier);
} else if (searchType == SEARCH_TYPE.CHILDREN_VIA_IUS_WORKFLOW_RUN) {
eList = handleWorkflowRunsViaIUS(files, copier);
} else if (searchType == SEARCH_TYPE.CHILDREN_VIA_LANE_WORKFLOW_RUN) {
eList = handleWorkflowRunsViaLane(files, copier);
}
final Document line = XmlTools.marshalToDocument(jaxbTool, eList);
getResponse().setEntity(XmlTools.getRepresentation(line));
getResponse().setStatus(Status.SUCCESS_CREATED);
}
protected WorkflowRunList2 handleWorkflowRunsViaChild(final Set<File> files, final Hibernate3DtoCopier copier) {
final WorkflowRunList2 results = new WorkflowRunList2();
for (File file : files) {
// 1) check if we have children in the processing tree that are relevant
for (Processing p : file.getProcessings()) {
for (Processing c : p.getChildren()) {
if (c.getWorkflowRun() == null) {
continue;
}
WorkflowRun dto = copier.hibernate2dto(WorkflowRun.class, c.getWorkflowRun());
results.add(dto);
}
}
}
if (results.getList().size() > 0) {
Log.debug("Found " + results.getList().size() + " workflow runs via Processing children");
} else {
Log.debug("Did not find any workflow runs via Processing children");
}
return results;
}
protected WorkflowRunList2 handleWorkflowRunsViaIUS(Set<File> files, final Hibernate3DtoCopier copier) {
WorkflowRunList2 result = new WorkflowRunList2();
Set<WorkflowRun> parentWorkflowRuns = new HashSet<>();
for (File file : files) {
// 2) check if we have children in the ius_workflow_runs that are relevant
for (Processing p : file.getProcessings()) {
WorkflowRun parentRun = p.getWorkflowRun();
if (parentRun == null) {
parentRun = p.getWorkflowRunByAncestorWorkflowRunId();
}
if (parentRun == null) {
continue;
}
parentWorkflowRuns.add(parentRun);
for (IUS ius : parentRun.getIus()) {
for (WorkflowRun anyRun : ius.getWorkflowRuns()) {
isWorkflowRunAttached(parentWorkflowRuns, anyRun);
WorkflowRun dto = copier.hibernate2dto(WorkflowRun.class, anyRun);
result.add(dto);
}
}
}
}
if (result.getList().size() > 0) {
Log.debug("Found " + result.getList().size() + " workflow runs via ius");
} else {
Log.debug("Did not find any workflow runs via ius");
}
return result;
}
protected WorkflowRunList2 handleWorkflowRunsViaLane(final Set<File> files, final Hibernate3DtoCopier copier) {
final WorkflowRunList2 result = new WorkflowRunList2();
Set<WorkflowRun> parentWorkflowRuns = new HashSet<>();
for (File file : files) {
// 3) check if we have children in the lane_workflow_runs that are relevant
for (Processing p : file.getProcessings()) {
WorkflowRun parentRun = p.getWorkflowRun();
if (parentRun == null) {
parentRun = p.getWorkflowRunByAncestorWorkflowRunId();
}
if (parentRun == null) {
continue;
}
parentWorkflowRuns.add(parentRun);
for (Lane lane : parentRun.getLanes()) {
for (WorkflowRun anyRun : lane.getWorkflowRuns()) {
if (isWorkflowRunAttached(parentWorkflowRuns, anyRun)) continue;
final WorkflowRun dto = copier.hibernate2dto(WorkflowRun.class, anyRun);
result.add(dto);
}
}
}
}
if (result.getList().size() > 0) {
Log.debug("Found " + result.getList().size() + " workflow runs via lane");
} else {
Log.debug("Did not find any workflow runs via lane");
}
return result;
}
/**
* Disallows a workflow run if it should not be considered since it is properly attached to the processing hierarchy
*
* @param parentWorkflowRuns
* @param anyRun
* @return
*/
private boolean isWorkflowRunAttached(Set<WorkflowRun> parentWorkflowRuns, WorkflowRun anyRun) {
if (parentWorkflowRuns.contains(anyRun)) {
Log.debug("Disallowed " + anyRun.getSwAccession() + " because we have seen it on the same level as a file");
return true;
}
// check that this workflow run has not actually been linked up into the Processing hierarchy
if (anyRun.getProcessings() != null && anyRun.getProcessings().size() > 0) {
Log.debug("Disallowed " + anyRun.getSwAccession() + " because it is already attached via Processing.workflow_run_id");
return true;
}
if (anyRun.getOffspringProcessings() != null && anyRun.getOffspringProcessings().size() > 0) {
Log.debug("Disallowed " + anyRun.getSwAccession() + " because it is already attached via Processing.ancestor_workflow_run_id");
return true;
}
return false;
}
/**
* Use SQL to directly retrieve relevant workflows runs (defined as any workflow runs that include one or more files in the set we were
* given)
*
* @param files
* @param interestingWorkflows
* @return
* @throws SQLException
*/
protected static WorkflowRunList2 directRetrieveWorkflowRuns(List<Integer> files, List<Integer> interestingWorkflows)
throws SQLException {
final Hibernate3DtoCopier copier = new Hibernate3DtoCopier();
final WorkflowRunList2 runs = new WorkflowRunList2();
runs.setList(new ArrayList());
if (files.size() > 0) {
ResultSet rs = null;
MetadataDB mdb = null;
try {
WorkflowRunService ss = BeanFactory.getWorkflowRunServiceBean();
StringBuilder query = new StringBuilder();
query.append("select distinct r.sw_accession from workflow_run r, workflow w, ");
query.append("workflow_run_input_files rf, file f WHERE r.workflow_run_id = rf.workflow_run_id "
+ "AND rf.file_id = f.file_id " + "AND w.workflow_id = r.workflow_id " + "AND (");
// handle file accessions
for (int i = 0; i < files.size() - 1; i++) {
Integer fInt = files.get(i);
query.append(" f.sw_accession = ").append(fInt).append(" OR");
}
Integer fInt = files.get(files.size() - 1);
query.append(" f.sw_accession = ").append(fInt).append(")");
// handle interesting workflow accessions
if (interestingWorkflows.size() > 0) {
query.append(" AND (");
for (int i = 0; i < interestingWorkflows.size() - 1; i++) {
Integer wInt = interestingWorkflows.get(i);
query.append(" w.sw_accession = ").append(wInt).append(" OR");
}
Integer wInt = interestingWorkflows.get(interestingWorkflows.size() - 1);
query.append(" w.sw_accession = ").append(wInt).append(")");
}
query.append(" ORDER BY sw_accession");
Log.info("Executing query: " + query);
mdb = DBAccess.get();
List<Integer> workflowSWIDs = mdb.executeQuery(query.toString(), new ResultSetHandler<List<Integer>>() {
@Override
public List<Integer> handle(ResultSet rs) throws SQLException {
List<Integer> ids = new ArrayList<>();
while (rs.next()) {
ids.add(rs.getInt("sw_accession"));
}
return ids;
}
});
for (int workflowSWID : workflowSWIDs) {
WorkflowRun workflowRun = (WorkflowRun) testIfNull(ss.findBySWAccession(workflowSWID));
CollectionPropertyName<WorkflowRun>[] createCollectionPropertyNames = CollectionPropertyName
.createCollectionPropertyNames(WorkflowRun.class, new String[] { "inputFileAccessions" });
WorkflowRun dto = copier.hibernate2dto(WorkflowRun.class, workflowRun, ArrayUtils.EMPTY_CLASS_ARRAY,
createCollectionPropertyNames);
runs.add(dto);
}
} finally {
if (mdb != null) {
DbUtils.closeQuietly(mdb.getDb(), mdb.getSql(), rs);
}
DBAccess.close();
}
}
return runs;
}
private void handleDirectGetXML() throws SQLException, NumberFormatException {
JaxbObject jaxbTool;
Log.info("Using direct search");
List<Integer> files = new ArrayList<>();
for (String key : queryValues.keySet()) {
Log.debug("key: " + key + " -> " + queryValues.get(key));
if (key.equals("files")) {
String value = queryValues.get(key);
String[] filesSWIDs = value.split(",");
for (String swid : filesSWIDs) {
files.add(Integer.valueOf(swid));
}
}
}
Log.debug("Working with " + files.size() + " files");
WorkflowRunList2 runs = directRetrieveWorkflowRuns(files, new ArrayList<Integer>());
// these variables will be used to return information
jaxbTool = new JaxbObject<>();
Log.debug("JaxbObjects started");
assert runs.getList().isEmpty();
final Document line = XmlTools.marshalToDocument(jaxbTool, runs);
getResponse().setEntity(XmlTools.getRepresentation(line));
getResponse().setStatus(Status.SUCCESS_CREATED);
}
public enum SEARCH_TYPE {
CHILDREN_VIA_PROCESSING_RELATIONSHIP, CHILDREN_VIA_IUS_WORKFLOW_RUN, CHILDREN_VIA_LANE_WORKFLOW_RUN
}
}