merge master

This commit is contained in:
Mark DePristo 2011-10-25 16:08:39 -04:00
commit 1b722c21cf
103 changed files with 3604 additions and 1342 deletions

138
build.xml
View File

@ -28,6 +28,8 @@
<property name="build.dir" value="build" />
<property name="dist.dir" value="dist" />
<property name="contract.dump.dir" value="dump" />
<property name="pipelinetest.dir" value="pipelinetests" />
<property name="lib.dir" value="lib" />
<property name="external.dir" value="external" />
<property name="public.dir" value="public" />
@ -35,18 +37,25 @@
<property name="java.public.source.dir" value="${public.dir}/java/src" />
<property name="java.private.source.dir" value="${private.dir}/java/src" />
<property name="java.classes" value="${build.dir}/java/classes" />
<property name="R.public.scripts.dir" value="${public.dir}/R/scripts" />
<property name="R.public.src.dir" value="${public.dir}/R/src" />
<!-- Legacy: Installing libraries back into the source directory
instead of the build or dist directory... intentionally avoids ant clean?? -->
<property name="R.library.dir" value="${public.dir}/R" />
<property name="R.tar.dir" value="${build.dir}/R/src" />
<property name="R.package.path" value="org/broadinstitute/sting/utils/R" />
<property name="resource.file" value="StingText.properties" />
<property name="resource.path" value="${java.classes}/StingText.properties" />
<property name="scala.public.source.dir" value="${public.dir}/scala/src" />
<property name="scala.private.source.dir" value="${private.dir}/scala/src" />
<property name="scala.private.source.dir" value="${private.dir}/scala/src" />
<property name="scala.classes" value="${build.dir}/scala/classes" />
<property name="queue-extensions.source.dir" value="${build.dir}/queue-extensions/src" />
<property name="javadoc.dir" value="javadoc" />
<property name="scaladoc.dir" value="scaladoc" />
<!-- Contracts for Java -->
<!-- By default, enabled only for test targets -->
<!-- To disable for test targets, run with -Duse.contracts=false -->
@ -60,7 +69,7 @@
<!-- do we want to halt on failure of a unit test? default to yes (Bamboo uses 'no') -->
<property name="halt" value="yes" />
<!-- should our unit test output go to a file or the screen?
false means it goes to the screen (default) true to file -->
<property name="usefile" value="false" />
@ -82,7 +91,7 @@
<patternset refid="java.source.pattern" />
</fileset>
<!-- terrible hack to get gatkdocs to see all files -->
<!-- terrible hack to get gatkdocs to see all files -->
<patternset id="all.java.source.pattern">
<include name="${java.public.source.dir}/**/*.java" />
<include name="${java.private.source.dir}/**/*.java" />
@ -113,7 +122,7 @@
<exclude name="testng*.jar" />
<exclude name="bcel*.jar" />
</patternset>
<path id="external.dependencies">
<fileset dir="${lib.dir}">
<patternset refid="dependency.mask" />
@ -154,7 +163,7 @@
<property name="ivy.jar.file" value="ivy-${ivy.install.version}.jar"/>
<property name="ivy.settings.dir" value="settings"/>
<property file="${ivy.settings.dir}/ivysettings.properties"/>
<mkdir dir="${lib.dir}"/>
<mkdir dir="${ivy.jar.dir}"/>
@ -211,11 +220,11 @@
<equals arg1="${git.describe.exit.value}" arg2="0" />
</condition>
</target>
<target name="tagged.build.version" depends="git.describe" if="git.describe.succeeded">
<property name="build.version" value="${git.describe.output}" />
</target>
<target name="git.rev-parse" depends="git.describe" unless="git.describe.succeeded">
<exec executable="git" outputproperty="git.rev-parse.output" resultproperty="git.rev-parse.exit.value" failonerror="false">
<arg line="rev-parse HEAD" />
@ -224,11 +233,11 @@
<equals arg1="${git.rev-parse.exit.value}" arg2="0" />
</condition>
</target>
<target name="untagged.build.version" depends="git.rev-parse" if="git.rev-parse.succeeded">
<property name="build.version" value="${git.rev-parse.output}" />
</target>
<target name="generate.build.version" depends="tagged.build.version, untagged.build.version">
<!-- Set build.version to exported if no other value has been set -->
<property name="build.version" value="exported" />
@ -266,7 +275,7 @@
<echo message="Scala build : ${scala.target}"/>
<echo message="source revision : ${build.version}"/>
<echo message="build time : ${build.timestamp}" />
<condition property="include.private">
<equals arg1="${gatk.target}" arg2="private" casesensitive="false" />
</condition>
@ -312,13 +321,13 @@
<target name="gatk.compile.public.source" depends="init,resolve">
<javac fork="true" srcdir="${java.public.source.dir}" memoryMaximumSize="512m" destdir="${java.classes}" debug="true" debuglevel="lines,vars,source" classpathref="external.dependencies" tempdir="${java.io.tmpdir}">
<compilerarg value="-proc:none"/>
</javac>
</javac>
</target>
<target name="gatk.compile.private.source" depends="gatk.compile.public.source" if="include.private">
<javac fork="true" srcdir="${java.private.source.dir}" memoryMaximumSize="512m" destdir="${java.classes}" debug="true" debuglevel="lines,vars,source" classpathref="external.dependencies" tempdir="${java.io.tmpdir}">
<compilerarg value="-proc:none"/>
</javac>
</javac>
</target>
<target name="gatk.compile.external.source" depends="gatk.compile.public.source,gatk.compile.private.source">
@ -327,11 +336,11 @@
<property name="dist.dir" value="${external.dist.dir}" />
<property name="gatk.classpath" value="${external.gatk.classpath}" />
<fileset dir="${external.dir}" includes="*/build.xml" erroronmissingdir="false" />
</subant>
</subant>
</target>
<target name="gatk.compile.source"
depends="gatk.compile.public.source,gatk.compile.private.source,gatk.compile.external.source"
<target name="gatk.compile.source"
depends="gatk.compile.public.source,gatk.compile.private.source,gatk.compile.external.source"
description="compile the GATK source" />
<target name="gatk.contracts.public" depends="gatk.compile.source" if="include.contracts">
@ -341,9 +350,9 @@
<pathelement path="${java.classes}" />
</classpath>
<compilerarg value="-Acom.google.java.contract.debug"/>
<compilerarg value="-Acom.google.java.contract.dump=dump/"/>
<compilerarg value="-Acom.google.java.contract.dump=${contract.dump.dir}"/>
<compilerarg value="-proc:only"/>
</javac>
</javac>
</target>
<target name="check.contracts.private" depends="gatk.contracts.public">
@ -362,14 +371,14 @@
<pathelement path="${java.classes}" />
</classpath>
<compilerarg value="-Acom.google.java.contract.debug"/>
<compilerarg value="-Acom.google.java.contract.dump=dump/"/>
<compilerarg value="-Acom.google.java.contract.dump=${contract.dump.dir}"/>
<compilerarg value="-proc:only"/>
</javac>
</javac>
</target>
<target name="gatk.contracts" depends="gatk.contracts.public,gatk.contracts.private"
<target name="gatk.contracts" depends="gatk.contracts.public,gatk.contracts.private"
description="create GATK contracts" if="include.contracts" />
<target name="gatk.compile" depends="init,resolve,gatk.compile.source,gatk.contracts" />
<target name="init.queue-extensions.generate" depends="gatk.compile">
@ -413,9 +422,9 @@
<src path="${scala.public.source.dir}" />
<src path="${queue-extensions.source.dir}" />
<include name="**/*.scala"/>
</scalac>
</scalac>
</target>
<target name="check.scala.private" depends="scala.compile.public">
<condition property="include.scala.private">
<and>
@ -424,12 +433,12 @@
</and>
</condition>
</target>
<target name="scala.compile.private" depends="check.scala.private" if="include.scala.private">
<scalac fork="true" jvmargs="-Xmx512m" destdir="${scala.classes}" classpathref="scala.dependencies" deprecation="yes" unchecked="yes">
<src path="${scala.private.source.dir}" />
<include name="**/*.scala"/>
</scalac>
</scalac>
</target>
<target name="scala.compile" depends="scala.compile.public,scala.compile.private" if="scala.include" description="compile Scala" />
@ -532,6 +541,11 @@
<target name="sting.compile" depends="gatk.compile, scala.compile" />
<target name="R.public.tar">
<mkdir dir="${R.tar.dir}/${R.package.path}" />
<tar compression="gzip" basedir="${R.public.src.dir}/${R.package.path}" includes="gsalib/**" destfile="${R.tar.dir}/${R.package.path}/gsalib.tar.gz" />
</target>
<target name="init.jar" depends="sting.compile,extracthelp">
<mkdir dir="${dist.dir}"/>
<copy todir="${dist.dir}">
@ -539,7 +553,7 @@
</copy>
</target>
<target name="sting-utils.jar" depends="gatk.compile, init.jar">
<target name="sting-utils.jar" depends="gatk.compile, init.jar, R.public.tar">
<jar jarfile="${dist.dir}/StingUtils.jar">
<fileset dir="${java.classes}">
<include name="**/utils/**/*.class"/>
@ -551,6 +565,12 @@
<fileset dir="${java.classes}" includes="**/sting/jna/**/*.class"/>
<fileset dir="${java.classes}" includes="net/sf/picard/**/*.class"/>
<fileset dir="${java.classes}" includes="net/sf/samtools/**/*.class"/>
<fileset dir="${R.tar.dir}">
<include name="**/${R.package.path}/**/*.tar.gz"/>
</fileset>
<fileset dir="${R.public.scripts.dir}">
<include name="**/utils/**/*.R"/>
</fileset>
<manifest>
<attribute name="Premain-Class" value="org.broadinstitute.sting.utils.instrumentation.Sizeof" />
</manifest>
@ -579,6 +599,10 @@
<include name="**/gatk/**/*.class" />
<include name="**/alignment/**/*.class"/>
</fileset>
<fileset dir="${R.public.scripts.dir}">
<include name="**/gatk/**/*.R"/>
<include name="**/alignment/**/*.R"/>
</fileset>
<manifest>
<attribute name="Main-Class" value="org.broadinstitute.sting.gatk.CommandLineGATK" />
</manifest>
@ -593,6 +617,10 @@
<include name="**/analyzecovariates/**/*.class" />
<include name="**/gatk/walkers/recalibration/*.class" />
</fileset>
<fileset dir="${R.public.scripts.dir}">
<include name="**/analyzecovariates/**/*.R"/>
<include name="**/gatk/walkers/recalibration/**/*.R"/>
</fileset>
<manifest>
<attribute name="Main-Class" value="org.broadinstitute.sting.analyzecovariates.AnalyzeCovariates" />
</manifest>
@ -605,28 +633,7 @@
<fileset dir="${external.dir}" includes="*/build.xml" erroronmissingdir="false" />
</subant>
</target>
<!--
<target name="gatk.oneoffs.jar" depends="gatk.compile, init.jar"
description="generate the GATK oneoffs distribution" if="include.oneoffs">
<jar jarfile="${dist.dir}/CompareBAMAlignments.jar" whenmanifestonly="skip">
<fileset dir="${java.classes}">
<include name="**/tools/**/*.class" />
</fileset>
<manifest>
<attribute name="Main-Class" value="org.broadinstitute.sting.oneoffprojects.tools.CompareBAMAlignments" />
</manifest>
</jar>
<jar jarfile="${dist.dir}/SliceBams.jar" whenmanifestonly="skip">
<fileset dir="${java.classes}">
<include name="**/tools/**/*.class" />
</fileset>
<manifest>
<attribute name="Main-Class" value="org.broadinstitute.sting.playground.tools.SliceBams" />
</manifest>
</jar>
</target>
-->
<target name="scala.jar" depends="scala.compile, init.jar" if="scala.include">
<jar jarfile="${dist.dir}/GATKScala.jar">
<fileset dir="${scala.classes}">
@ -643,6 +650,9 @@
<fileset dir="${java.classes}">
<include name="org/broadinstitute/sting/queue/**/*.class" />
</fileset>
<fileset dir="${R.public.scripts.dir}">
<include name="org/broadinstitute/sting/queue/**/*.R"/>
</fileset>
<manifest>
<attribute name="Main-Class" value="org.broadinstitute.sting.queue.QCommandLine" />
</manifest>
@ -682,20 +692,7 @@
</jar>
</target>
<!--
<target name="gatk.oneoffs.manifests" depends="gatk.oneoffs.jar, init.manifests" if="include.oneoffs">
<jar jarfile="${dist.dir}/CompareBAMAlignments.jar" update="true" whenmanifestonly="skip">
<manifest>
<attribute name="Class-Path" value="${jar.classpath}" />
</manifest>
</jar>
<jar jarfile="${dist.dir}/SliceBams.jar" update="true" whenmanifestonly="skip">
<manifest>
<attribute name="Class-Path" value="${jar.classpath}" />
</manifest>
</jar>
</target>
-->
<target name="queue.manifests" depends="queue.jar, init.manifests" if="scala.include">
<jar jarfile="${dist.dir}/Queue.jar" update="true" >
<manifest>
@ -780,10 +777,6 @@
<pathelement location="${testng.jar}"/>
</classpath>
<compilerarg value="-proc:none"/>
<!--
<compilerarg value="-Acom.google.java.contract.debug"/>
<compilerarg value="-Acom.google.java.contract.dump=dump/"/>
-->
</javac>
</target>
@ -800,10 +793,6 @@
<pathelement location="${testng.jar}"/>
</classpath>
<compilerarg value="-proc:none"/>
<!--
<compilerarg value="-Acom.google.java.contract.debug"/>
<compilerarg value="-Acom.google.java.contract.dump=dump/"/>
-->
</javac>
</target>
@ -851,6 +840,8 @@
<pathelement location="${java.private.test.classes}" />
<pathelement location="${scala.public.test.classes}" />
<pathelement location="${scala.private.test.classes}" />
<pathelement location="${R.tar.dir}" />
<pathelement location="${R.public.scripts.dir}" />
</path>
<path id="testng.gatk.releasetest.classpath">
@ -1187,19 +1178,18 @@
</target>
<target name="clean" description="clean up" depends="clean.javadoc,clean.scaladoc,clean.gatkdocs">
<delete dir="out"/>
<delete dir="${build.dir}"/>
<delete dir="${lib.dir}"/>
<delete dir="dump"/>
<delete dir="${contract.dump.dir}"/>
<delete dir="${staging.dir}"/>
<delete dir="${dist.dir}"/>
<delete dir="pipelinetests"/>
<delete dir="${pipelinetest.dir}"/>
</target>
<!-- Build gsalib R module -->
<target name="gsalib">
<exec executable="R" failonerror="true">
<arg line="R CMD INSTALL -l public/R/ public/R/src/gsalib/" />
<arg line="R CMD INSTALL -l ${R.library.dir} ${R.public.src.dir}/${R.package.path}/gsalib" />
</exec>
</target>
</project>

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -46,7 +46,7 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
/**
* Maps indices of command line arguments to values paired with that argument.
*/
public final SortedMap<Integer,List<String>> indices = new TreeMap<Integer,List<String>>();
public final SortedMap<ArgumentMatchSite,List<String>> sites = new TreeMap<ArgumentMatchSite,List<String>>();
/**
* An ordered, freeform collection of tags.
@ -72,32 +72,32 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
}
/**
* A simple way of indicating that an argument with the given label and definition exists at this index.
* A simple way of indicating that an argument with the given label and definition exists at this site.
* @param label Label of the argument match. Must not be null.
* @param definition The associated definition, if one exists. May be null.
* @param index Position of the argument. Must not be null.
* @param site Position of the argument. Must not be null.
* @param tags ordered freeform text tags associated with this argument.
*/
public ArgumentMatch(final String label, final ArgumentDefinition definition, final int index, final Tags tags) {
this( label, definition, index, null, tags );
public ArgumentMatch(final String label, final ArgumentDefinition definition, final ArgumentMatchSite site, final Tags tags) {
this( label, definition, site, null, tags );
}
/**
* A simple way of indicating that an argument with the given label and definition exists at this index.
* A simple way of indicating that an argument with the given label and definition exists at this site.
* @param label Label of the argument match. Must not be null.
* @param definition The associated definition, if one exists. May be null.
* @param index Position of the argument. Must not be null.
* @param site Position of the argument. Must not be null.
* @param value Value for the argument at this position.
* @param tags ordered freeform text tags associated with this argument.
*/
private ArgumentMatch(final String label, final ArgumentDefinition definition, final int index, final String value, final Tags tags) {
private ArgumentMatch(final String label, final ArgumentDefinition definition, final ArgumentMatchSite site, final String value, final Tags tags) {
this.label = label;
this.definition = definition;
ArrayList<String> values = new ArrayList<String>();
if( value != null )
values.add(value);
indices.put(index,values );
sites.put(site,values );
this.tags = tags;
}
@ -117,7 +117,7 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
ArgumentMatch otherArgumentMatch = (ArgumentMatch)other;
return this.definition.equals(otherArgumentMatch.definition) &&
this.label.equals(otherArgumentMatch.label) &&
this.indices.equals(otherArgumentMatch.indices) &&
this.sites.equals(otherArgumentMatch.sites) &&
this.tags.equals(otherArgumentMatch.tags);
}
@ -129,16 +129,17 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
* @param key Key which specifies the transform.
* @return A variant of this ArgumentMatch with all keys transformed.
*/
@SuppressWarnings("unchecked")
ArgumentMatch transform(Multiplexer multiplexer, Object key) {
SortedMap<Integer,List<String>> newIndices = new TreeMap<Integer,List<String>>();
for(Map.Entry<Integer,List<String>> index: indices.entrySet()) {
SortedMap<ArgumentMatchSite,List<String>> newIndices = new TreeMap<ArgumentMatchSite,List<String>>();
for(Map.Entry<ArgumentMatchSite,List<String>> site: sites.entrySet()) {
List<String> newEntries = new ArrayList<String>();
for(String entry: index.getValue())
for(String entry: site.getValue())
newEntries.add(multiplexer.transformArgument(key,entry));
newIndices.put(index.getKey(),newEntries);
newIndices.put(site.getKey(),newEntries);
}
ArgumentMatch newArgumentMatch = new ArgumentMatch(label,definition);
newArgumentMatch.indices.putAll(newIndices);
newArgumentMatch.sites.putAll(newIndices);
return newArgumentMatch;
}
@ -157,9 +158,9 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
public Iterator<ArgumentMatch> iterator() {
return new Iterator<ArgumentMatch>() {
/**
* Iterate over each the available index.
* Iterate over each the available site.
*/
private Iterator<Integer> indexIterator = null;
private Iterator<ArgumentMatchSite> siteIterator = null;
/**
* Iterate over each available token.
@ -167,9 +168,9 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
private Iterator<String> tokenIterator = null;
/**
* The next index to return. Null if none remain.
* The next site to return. Null if none remain.
*/
Integer nextIndex = null;
ArgumentMatchSite nextSite = null;
/**
* The next token to return. Null if none remain.
@ -177,7 +178,7 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
String nextToken = null;
{
indexIterator = indices.keySet().iterator();
siteIterator = sites.keySet().iterator();
prepareNext();
}
@ -186,7 +187,7 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
* @return True if there's another token waiting in the wings. False otherwise.
*/
public boolean hasNext() {
return nextToken != null;
return nextToken != null;
}
/**
@ -194,32 +195,32 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
* @return The next ArgumentMatch in the series. Should never be null.
*/
public ArgumentMatch next() {
if( nextIndex == null || nextToken == null )
if( nextSite == null || nextToken == null )
throw new IllegalStateException( "No more ArgumentMatches are available" );
ArgumentMatch match = new ArgumentMatch( label, definition, nextIndex, nextToken, tags );
ArgumentMatch match = new ArgumentMatch( label, definition, nextSite, nextToken, tags );
prepareNext();
return match;
}
/**
* Initialize the next ArgumentMatch to return. If no ArgumentMatches are available,
* initialize nextIndex / nextToken to null.
* initialize nextSite / nextToken to null.
*/
private void prepareNext() {
if( tokenIterator != null && tokenIterator.hasNext() ) {
nextToken = tokenIterator.next();
}
else {
nextIndex = null;
nextSite = null;
nextToken = null;
// Do a nested loop. While more data is present in the inner loop, grab that data.
// Otherwise, troll the outer iterator looking for more data.
while( indexIterator.hasNext() ) {
nextIndex = indexIterator.next();
if( indices.get(nextIndex) != null ) {
tokenIterator = indices.get(nextIndex).iterator();
while( siteIterator.hasNext() ) {
nextSite = siteIterator.next();
if( sites.get(nextSite) != null ) {
tokenIterator = sites.get(nextSite).iterator();
if( tokenIterator.hasNext() ) {
nextToken = tokenIterator.next();
break;
@ -245,29 +246,29 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
* @param other The other match to merge into.
*/
public void mergeInto( ArgumentMatch other ) {
indices.putAll(other.indices);
sites.putAll(other.sites);
}
/**
* Associate a value with this merge maapping.
* @param index index of the command-line argument to which this value is mated.
* @param site site of the command-line argument to which this value is mated.
* @param value Text representation of value to add.
*/
public void addValue( int index, String value ) {
if( !indices.containsKey(index) || indices.get(index) == null )
indices.put(index, new ArrayList<String>() );
indices.get(index).add(value);
public void addValue( ArgumentMatchSite site, String value ) {
if( !sites.containsKey(site) || sites.get(site) == null )
sites.put(site, new ArrayList<String>() );
sites.get(site).add(value);
}
/**
* Does this argument already have a value at the given site?
* Arguments are only allowed to be single-valued per site, and
* flags aren't allowed a value at all.
* @param index Index at which to check for values.
* @param site Site at which to check for values.
* @return True if the argument has a value at the given site. False otherwise.
*/
public boolean hasValueAtSite( int index ) {
return (indices.get(index) != null && indices.get(index).size() >= 1) || isArgumentFlag();
public boolean hasValueAtSite( ArgumentMatchSite site ) {
return (sites.get(site) != null && sites.get(site).size() >= 1) || isArgumentFlag();
}
/**
@ -276,9 +277,9 @@ public class ArgumentMatch implements Iterable<ArgumentMatch> {
*/
public List<String> values() {
List<String> values = new ArrayList<String>();
for( int index: indices.keySet() ) {
if( indices.get(index) != null )
values.addAll(indices.get(index));
for( ArgumentMatchSite site: sites.keySet() ) {
if( sites.get(site) != null )
values.addAll(sites.get(site));
}
return values;
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.commandline;
/**
* Which source and the index within the source where an argument match was found.
*/
public class ArgumentMatchSite implements Comparable<ArgumentMatchSite> {
private final ArgumentMatchSource source;
private final int index;
public ArgumentMatchSite(ArgumentMatchSource source, int index) {
this.source = source;
this.index = index;
}
public ArgumentMatchSource getSource() {
return source;
}
public int getIndex() {
return index;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ArgumentMatchSite that = (ArgumentMatchSite) o;
return (index == that.index) && (source == null ? that.source == null : source.equals(that.source));
}
@Override
public int hashCode() {
int result = source != null ? source.hashCode() : 0;
// Generated by intellij. No other special reason to this implementation. -ks
result = 31 * result + index;
return result;
}
@Override
public int compareTo(ArgumentMatchSite that) {
int comp = this.source.compareTo(that.source);
if (comp != 0)
return comp;
// Both files are the same.
if (this.index == that.index)
return 0;
return this.index < that.index ? -1 : 1;
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.commandline;
import java.io.File;
/**
* Where an argument match originated, via the commandline or a file.
*/
public class ArgumentMatchSource implements Comparable<ArgumentMatchSource> {
public static final ArgumentMatchSource COMMAND_LINE = new ArgumentMatchSource(ArgumentMatchSourceType.CommandLine, null);
private final ArgumentMatchSourceType type;
private final File file;
/**
* Creates an argument match source from the specified file.
* @param file File specifying the arguments. Must not be null.
*/
public ArgumentMatchSource(File file) {
this(ArgumentMatchSourceType.File, file);
}
private ArgumentMatchSource(ArgumentMatchSourceType type, File file) {
if (type == ArgumentMatchSourceType.File && file == null)
throw new IllegalArgumentException("An argument match source of type File cannot have a null file.");
this.type = type;
this.file = file;
}
public ArgumentMatchSourceType getType() {
return type;
}
public File getFile() {
return file;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ArgumentMatchSource that = (ArgumentMatchSource) o;
return (type == that.type) && (file == null ? that.file == null : file.equals(that.file));
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (file != null ? file.hashCode() : 0);
return result;
}
/**
* Compares two sources, putting the command line first, then files.
*/
@Override
public int compareTo(ArgumentMatchSource that) {
int comp = this.type.compareTo(that.type);
if (comp != 0)
return comp;
File f1 = this.file;
File f2 = that.file;
if ((f1 == null) ^ (f2 == null)) {
// If one of the files is null and the other is not
// put the null file first
return f1 == null ? -1 : 1;
}
return f1 == null ? 0 : f1.compareTo(f2);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.commandline;
/**
* Type of where an argument match originated, via the commandline or a file.
*/
public enum ArgumentMatchSourceType {
CommandLine, File
}

View File

@ -37,7 +37,7 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
* Collection matches from argument definition to argument value.
* Package protected access is deliberate.
*/
Map<Integer,ArgumentMatch> argumentMatches = new TreeMap<Integer,ArgumentMatch>();
Map<ArgumentMatchSite,ArgumentMatch> argumentMatches = new TreeMap<ArgumentMatchSite,ArgumentMatch>();
/**
* Provide a place to put command-line argument values that don't seem to belong to
@ -80,7 +80,7 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
* @param site Site at which to check.
* @return True if the site has a match. False otherwise.
*/
boolean hasMatch( int site ) {
boolean hasMatch( ArgumentMatchSite site ) {
return argumentMatches.containsKey( site );
}
@ -90,7 +90,7 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
* @return The match present at the given site.
* @throws IllegalArgumentException if site does not contain a match.
*/
ArgumentMatch getMatch( int site ) {
ArgumentMatch getMatch( ArgumentMatchSite site ) {
if( !argumentMatches.containsKey(site) )
throw new IllegalArgumentException( "Site does not contain an argument: " + site );
return argumentMatches.get(site);
@ -107,6 +107,7 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
/**
* Return all argument matches of this source.
* @param parsingEngine Parsing engine.
* @param argumentSource Argument source to match.
* @return List of all matches.
*/
@ -167,6 +168,7 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
* TODO: Generify this.
* @param multiplexer Multiplexer that controls the transformation process.
* @param key Key which specifies the transform.
* @return new argument matches.
*/
ArgumentMatches transform(Multiplexer multiplexer, Object key) {
ArgumentMatches newArgumentMatches = new ArgumentMatches();
@ -187,15 +189,15 @@ public class ArgumentMatches implements Iterable<ArgumentMatch> {
for( ArgumentMatch argumentMatch: getUniqueMatches() ) {
if( argumentMatch.definition == match.definition && argumentMatch.tags.equals(match.tags) ) {
argumentMatch.mergeInto( match );
for( int index: match.indices.keySet() )
argumentMatches.put( index, argumentMatch );
for( ArgumentMatchSite site: match.sites.keySet() )
argumentMatches.put( site, argumentMatch );
definitionExists = true;
}
}
if( !definitionExists ) {
for( int index: match.indices.keySet() )
argumentMatches.put( index, match );
for( ArgumentMatchSite site: match.sites.keySet() )
argumentMatches.put( site, match );
}
}

View File

@ -35,10 +35,7 @@ import org.broadinstitute.sting.utils.help.ApplicationDetails;
import org.broadinstitute.sting.utils.help.HelpFormatter;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Locale;
import java.util.*;
public abstract class CommandLineProgram {
@ -155,6 +152,7 @@ public abstract class CommandLineProgram {
*
* @param clp the command line program to execute
* @param args the command line arguments passed in
* @param dryRun dry run
* @throws Exception when an exception occurs
*/
@SuppressWarnings("unchecked")
@ -176,6 +174,8 @@ public abstract class CommandLineProgram {
ParsingEngine parser = clp.parser = new ParsingEngine(clp);
parser.addArgumentSource(clp.getClass());
Map<ArgumentMatchSource, List<String>> parsedArgs;
// process the args
if (clp.canAddArgumentsDynamically()) {
// if the command-line program can toss in extra args, fetch them and reparse the arguments.
@ -196,14 +196,14 @@ public abstract class CommandLineProgram {
Class[] argumentSources = clp.getArgumentSources();
for (Class argumentSource : argumentSources)
parser.addArgumentSource(clp.getArgumentSourceName(argumentSource), argumentSource);
parser.parse(args);
parsedArgs = parser.parse(args);
if (isHelpPresent(parser))
printHelpAndExit(clp, parser);
if ( ! dryRun ) parser.validate();
} else {
parser.parse(args);
parsedArgs = parser.parse(args);
if ( ! dryRun ) {
if (isHelpPresent(parser))
@ -230,7 +230,7 @@ public abstract class CommandLineProgram {
}
// regardless of what happens next, generate the header information
HelpFormatter.generateHeaderInformation(clp.getApplicationDetails(), args);
HelpFormatter.generateHeaderInformation(clp.getApplicationDetails(), parsedArgs);
// call the execute
CommandLineProgram.result = clp.execute();

View File

@ -26,6 +26,7 @@
package org.broadinstitute.sting.commandline;
import com.google.java.contract.Requires;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.broadinstitute.sting.utils.Utils;
import org.broadinstitute.sting.utils.classloader.JVMUtils;
@ -35,6 +36,8 @@ import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.help.ApplicationDetails;
import org.broadinstitute.sting.utils.help.HelpFormatter;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
@ -100,6 +103,8 @@ public class ParsingEngine {
if(clp != null)
argumentTypeDescriptors.addAll(clp.getArgumentTypeDescriptors());
argumentTypeDescriptors.addAll(STANDARD_ARGUMENT_TYPE_DESCRIPTORS);
addArgumentSource(ParsingEngineArgumentFiles.class);
}
/**
@ -148,21 +153,43 @@ public class ParsingEngine {
* command-line arguments to the arguments that are actually
* required.
* @param tokens Tokens passed on the command line.
* @return The parsed arguments by file.
*/
public void parse( String[] tokens ) {
public SortedMap<ArgumentMatchSource, List<String>> parse( String[] tokens ) {
argumentMatches = new ArgumentMatches();
SortedMap<ArgumentMatchSource, List<String>> parsedArgs = new TreeMap<ArgumentMatchSource, List<String>>();
int lastArgumentMatchSite = -1;
List<String> cmdLineTokens = Arrays.asList(tokens);
parse(ArgumentMatchSource.COMMAND_LINE, cmdLineTokens, argumentMatches, parsedArgs);
for( int i = 0; i < tokens.length; i++ ) {
String token = tokens[i];
ParsingEngineArgumentFiles argumentFiles = new ParsingEngineArgumentFiles();
// Load the arguments ONLY into the argument files.
// Validation may optionally run on the rest of the arguments.
loadArgumentsIntoObject(argumentFiles);
for (File file: argumentFiles.files) {
List<String> fileTokens = getArguments(file);
parse(new ArgumentMatchSource(file), fileTokens, argumentMatches, parsedArgs);
}
return parsedArgs;
}
private void parse(ArgumentMatchSource matchSource, List<String> tokens,
ArgumentMatches argumentMatches, SortedMap<ArgumentMatchSource, List<String>> parsedArgs) {
ArgumentMatchSite lastArgumentMatchSite = new ArgumentMatchSite(matchSource, -1);
int i = 0;
for (String token: tokens) {
// If the token is of argument form, parse it into its own argument match.
// Otherwise, pair it with the most recently used argument discovered.
ArgumentMatchSite site = new ArgumentMatchSite(matchSource, i);
if( isArgumentForm(token) ) {
ArgumentMatch argumentMatch = parseArgument( token, i );
ArgumentMatch argumentMatch = parseArgument( token, site );
if( argumentMatch != null ) {
argumentMatches.mergeInto( argumentMatch );
lastArgumentMatchSite = i;
lastArgumentMatchSite = site;
}
}
else {
@ -170,10 +197,31 @@ public class ParsingEngine {
!argumentMatches.getMatch(lastArgumentMatchSite).hasValueAtSite(lastArgumentMatchSite))
argumentMatches.getMatch(lastArgumentMatchSite).addValue( lastArgumentMatchSite, token );
else
argumentMatches.MissingArgument.addValue( i, token );
argumentMatches.MissingArgument.addValue( site, token );
}
i++;
}
parsedArgs.put(matchSource, tokens);
}
private List<String> getArguments(File file) {
try {
if (file.getAbsolutePath().endsWith(".list")) {
return getListArguments(file);
}
} catch (IOException e) {
throw new UserException.CouldNotReadInputFile(file, e);
}
throw new UserException.CouldNotReadInputFile(file, "file extension is not .list");
}
private List<String> getListArguments(File file) throws IOException {
ArrayList<String> argsList = new ArrayList<String>();
for (String line: FileUtils.readLines(file))
argsList.addAll(Arrays.asList(Utils.escapeExpressions(line)));
return argsList;
}
public enum ValidationType { MissingRequiredArgument,
@ -494,7 +542,7 @@ public class ParsingEngine {
* @param position The position of the token in question.
* @return ArgumentMatch associated with this token, or null if no match exists.
*/
private ArgumentMatch parseArgument( String token, int position ) {
private ArgumentMatch parseArgument( String token, ArgumentMatchSite position ) {
if( !isArgumentForm(token) )
throw new IllegalArgumentException( "Token is not recognizable as an argument: " + token );
@ -579,9 +627,21 @@ class UnmatchedArgumentException extends ArgumentException {
private static String formatArguments( ArgumentMatch invalidValues ) {
StringBuilder sb = new StringBuilder();
for( int index: invalidValues.indices.keySet() )
for( String value: invalidValues.indices.get(index) ) {
sb.append( String.format("%nInvalid argument value '%s' at position %d.", value, index) );
for( ArgumentMatchSite site: invalidValues.sites.keySet() )
for( String value: invalidValues.sites.get(site) ) {
switch (site.getSource().getType()) {
case CommandLine:
sb.append( String.format("%nInvalid argument value '%s' at position %d.",
value, site.getIndex()) );
break;
case File:
sb.append( String.format("%nInvalid argument value '%s' in file %s at position %d.",
value, site.getSource().getFile().getAbsolutePath(), site.getIndex()) );
break;
default:
throw new RuntimeException( String.format("Unexpected argument match source type: %s",
site.getSource().getType()));
}
if(value != null && Utils.dupString(' ',value.length()).equals(value))
sb.append(" Please make sure any line continuation backslashes on your command line are not followed by whitespace.");
}
@ -634,4 +694,13 @@ class UnknownEnumeratedValueException extends ArgumentException {
private static String formatArguments(ArgumentDefinition definition, String argumentPassed) {
return String.format("Invalid value %s specified for argument %s; valid options are (%s).", argumentPassed, definition.fullName, Utils.join(",",definition.validOptions));
}
}
}
/**
* Container class to store the list of argument files.
* The files will be parsed after the command line arguments.
*/
class ParsingEngineArgumentFiles {
@Argument(fullName = "arg_file", shortName = "args", doc = "Reads arguments from the specified file", required = false)
public List<File> files = new ArrayList<File>();
}

View File

@ -68,7 +68,7 @@ public abstract class ParsingMethod {
* @return An argument match. Definition field will be populated if a match was found or
* empty if no appropriate definition could be found.
*/
public ArgumentMatch match( ArgumentDefinitions definitions, String token, int position ) {
public ArgumentMatch match( ArgumentDefinitions definitions, String token, ArgumentMatchSite position ) {
// If the argument is valid, parse out the argument.
Matcher matcher = pattern.matcher(token);
@ -102,9 +102,7 @@ public abstract class ParsingMethod {
// Try to find a matching argument. If found, label that as the match. If not found, add the argument
// with a null definition.
ArgumentMatch argumentMatch = new ArgumentMatch(argument,argumentDefinition,position,tags);
return argumentMatch;
return new ArgumentMatch(argument,argumentDefinition,position,tags);
}
/**

View File

@ -220,12 +220,12 @@ public class GenomeAnalysisEngine {
ShardStrategy shardStrategy = getShardStrategy(readsDataSource,microScheduler.getReference(),intervals);
// execute the microscheduler, storing the results
Object result = microScheduler.execute(this.walker, shardStrategy);
return microScheduler.execute(this.walker, shardStrategy);
//monitor.stop();
//logger.info(String.format("Maximum heap size consumed: %d",monitor.getMaxMemoryUsed()));
return result;
//return result;
}
/**
@ -296,10 +296,14 @@ public class GenomeAnalysisEngine {
else if(WalkerManager.getDownsamplingMethod(walker) != null)
method = WalkerManager.getDownsamplingMethod(walker);
else
method = argCollection.getDefaultDownsamplingMethod();
method = GATKArgumentCollection.getDefaultDownsamplingMethod();
return method;
}
protected void setDownsamplingMethod(DownsamplingMethod method) {
argCollection.setDownsamplingMethod(method);
}
public BAQ.QualityMode getWalkerBAQQualityMode() { return WalkerManager.getBAQQualityMode(walker); }
public BAQ.ApplicationTime getWalkerBAQApplicationTime() { return WalkerManager.getBAQApplicationTime(walker); }
@ -389,7 +393,9 @@ public class GenomeAnalysisEngine {
/**
* Get the sharding strategy given a driving data source.
*
* @param readsDataSource readsDataSource
* @param drivingDataSource Data on which to shard.
* @param intervals intervals
* @return the sharding strategy
*/
protected ShardStrategy getShardStrategy(SAMDataSource readsDataSource, ReferenceSequenceFile drivingDataSource, GenomeLocSortedSet intervals) {
@ -426,7 +432,7 @@ public class GenomeAnalysisEngine {
return new MonolithicShardStrategy(getGenomeLocParser(), readsDataSource,shardType,region);
}
ShardStrategy shardStrategy = null;
ShardStrategy shardStrategy;
ShardStrategyFactory.SHATTER_STRATEGY shardType;
long SHARD_SIZE = 100000L;
@ -435,6 +441,8 @@ public class GenomeAnalysisEngine {
if (walker instanceof RodWalker) SHARD_SIZE *= 1000;
if (intervals != null && !intervals.isEmpty()) {
if (readsDataSource == null)
throw new IllegalArgumentException("readsDataSource is null");
if(!readsDataSource.isEmpty() && readsDataSource.getSortOrder() != SAMFileHeader.SortOrder.coordinate)
throw new UserException.MissortedBAM(SAMFileHeader.SortOrder.coordinate, "Locus walkers can only traverse coordinate-sorted data. Please resort your input BAM file(s) or set the Sort Order tag in the header appropriately.");
@ -498,7 +506,8 @@ public class GenomeAnalysisEngine {
*/
private void initializeTempDirectory() {
File tempDir = new File(System.getProperty("java.io.tmpdir"));
tempDir.mkdirs();
if (!tempDir.exists() && !tempDir.mkdirs())
throw new UserException.BadTmpDir("Unable to create directory");
}
/**
@ -729,6 +738,7 @@ public class GenomeAnalysisEngine {
* @param reads Reads data source.
* @param reference Reference data source.
* @param rods a collection of the reference ordered data tracks
* @param manager manager
*/
private void validateSourcesAgainstReference(SAMDataSource reads, ReferenceSequenceFile reference, Collection<ReferenceOrderedDataSource> rods, RMDTrackBuilder manager) {
if ((reads.isEmpty() && (rods == null || rods.isEmpty())) || reference == null )
@ -757,15 +767,22 @@ public class GenomeAnalysisEngine {
/**
* Gets a data source for the given set of reads.
*
* @param argCollection arguments
* @param genomeLocParser parser
* @param refReader reader
* @return A data source for the given set of reads.
*/
private SAMDataSource createReadsDataSource(GATKArgumentCollection argCollection, GenomeLocParser genomeLocParser, IndexedFastaSequenceFile refReader) {
DownsamplingMethod method = getDownsamplingMethod();
// Synchronize the method back into the collection so that it shows up when
// interrogating for the downsample method during command line recreation.
setDownsamplingMethod(method);
if ( getWalkerBAQApplicationTime() == BAQ.ApplicationTime.FORBIDDEN && argCollection.BAQMode != BAQ.CalculationMode.OFF)
throw new UserException.BadArgumentValue("baq", "Walker cannot accept BAQ'd base qualities, and yet BAQ mode " + argCollection.BAQMode + " was requested.");
SAMDataSource dataSource = new SAMDataSource(
return new SAMDataSource(
samReaderIDs,
genomeLocParser,
argCollection.useOriginalBaseQualities,
@ -781,14 +798,12 @@ public class GenomeAnalysisEngine {
refReader,
argCollection.defaultBaseQualities,
!argCollection.disableLowMemorySharding);
return dataSource;
}
/**
* Opens a reference sequence file paired with an index. Only public for testing purposes
*
* @param refFile Handle to a reference sequence file. Non-null.
* @return A thread-safe file wrapper.
*/
public void setReferenceDataSource(File refFile) {
this.referenceDataSource = new ReferenceDataSource(refFile);

View File

@ -135,8 +135,8 @@ public class GATKArgumentCollection {
/**
* Gets the downsampling method explicitly specified by the user. If the user didn't specify
* a default downsampling mechanism, return null.
* @return The explicitly specified downsampling mechanism, or null if none exists.
* a default downsampling mechanism, return the default.
* @return The explicitly specified downsampling mechanism, or the default if none exists.
*/
public DownsamplingMethod getDownsamplingMethod() {
if(downsamplingType == null && downsampleFraction == null && downsampleCoverage == null)
@ -146,6 +146,18 @@ public class GATKArgumentCollection {
return new DownsamplingMethod(downsamplingType,downsampleCoverage,downsampleFraction);
}
/**
* Set the downsampling method stored in the argument collection so that it is read back out when interrogating the command line arguments.
* @param method The downsampling mechanism.
*/
public void setDownsamplingMethod(DownsamplingMethod method) {
if (method == null)
throw new IllegalArgumentException("method is null");
downsamplingType = method.type;
downsampleCoverage = method.toCoverage;
downsampleFraction = method.toFraction;
}
// --------------------------------------------------------------------------------------------------------------
//
// BAQ arguments

View File

@ -27,7 +27,9 @@ package org.broadinstitute.sting.gatk.filters;
import net.sf.samtools.SAMFileHeader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.SAMSequenceRecord;
import org.broadinstitute.sting.commandline.Argument;
import org.broadinstitute.sting.gatk.GenomeAnalysisEngine;
import org.broadinstitute.sting.utils.exceptions.UserException;
/**
* Filter out malformed reads.
@ -37,14 +39,25 @@ import org.broadinstitute.sting.gatk.GenomeAnalysisEngine;
*/
public class MalformedReadFilter extends ReadFilter {
private SAMFileHeader header;
@Argument(fullName = "filter_mismatching_base_and_quals", shortName = "filterMBQ", doc = "if a read has mismatching number of bases and base qualities, filter out the read instead of blowing up.", required = false)
boolean filterMismatchingBaseAndQuals = false;
@Override
public void initialize(GenomeAnalysisEngine engine) {
this.header = engine.getSAMFileHeader();
}
public boolean filterOut(SAMRecord read) {
return !checkInvalidAlignmentStart(read) ||
// slowly changing the behavior to blow up first and filtering out if a parameter is explicitly provided
if (!checkMismatchingBasesAndQuals(read)) {
if (!filterMismatchingBaseAndQuals)
throw new UserException.MalformedBAM(read, "BAM file has a read with mismatching number of bases and base qualities. Offender: " + read.getReadName() +" [" + read.getReadLength() + " bases] [" +read.getBaseQualities().length +"] quals");
else
return true;
}
return !checkInvalidAlignmentStart(read) ||
!checkInvalidAlignmentEnd(read) ||
!checkAlignmentDisagreesWithHeader(this.header,read) ||
!checkCigarDisagreesWithAlignment(read);
@ -108,4 +121,13 @@ public class MalformedReadFilter extends ReadFilter {
return false;
return true;
}
/**
* Check if the read has the same number of bases and base qualities
* @param read the read to validate
* @return true if they have the same number. False otherwise.
*/
private static boolean checkMismatchingBasesAndQuals(SAMRecord read) {
return (read.getReadLength() == read.getBaseQualities().length);
}
}

View File

@ -475,7 +475,8 @@ public class ClipReadsWalker extends ReadWalker<ClipReadsWalker.ReadClipperWithD
}
public void onTraversalDone(ClippingData data) {
out.printf(data.toString());
if ( out != null )
out.printf(data.toString());
}
// --------------------------------------------------------------------------------------------------------------

View File

@ -32,11 +32,17 @@ import net.sf.samtools.SAMRecord;
import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
import org.broadinstitute.sting.utils.Haplotype;
import org.broadinstitute.sting.utils.MathUtils;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.pileup.PileupElement;
import org.broadinstitute.sting.utils.pileup.ReadBackedPileup;
import org.broadinstitute.sting.utils.sam.GATKSAMRecord;
import org.broadinstitute.sting.utils.sam.ReadUtils;
import org.broadinstitute.sting.utils.variantcontext.Allele;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
@ -45,9 +51,6 @@ import java.util.LinkedHashMap;
public class PairHMMIndelErrorModel {
public static final int BASE_QUAL_THRESHOLD = 20;
private final double logGapOpenProbability;
private final double logGapContinuationProbability;
private boolean DEBUG = false;
private boolean bandedLikelihoods = false;
@ -89,8 +92,6 @@ public class PairHMMIndelErrorModel {
}
public PairHMMIndelErrorModel(double indelGOP, double indelGCP, boolean deb, boolean bandedLikelihoods) {
this.logGapOpenProbability = -indelGOP/10.0; // QUAL to log prob
this.logGapContinuationProbability = -indelGCP/10.0; // QUAL to log prob
this.DEBUG = deb;
this.bandedLikelihoods = bandedLikelihoods;
@ -98,13 +99,14 @@ public class PairHMMIndelErrorModel {
this.GAP_CONT_PROB_TABLE = new double[MAX_HRUN_GAP_IDX];
this.GAP_OPEN_PROB_TABLE = new double[MAX_HRUN_GAP_IDX];
double gop = -indelGOP/10.0;
double gcp = -indelGCP/10.0;
for (int i = 0; i < START_HRUN_GAP_IDX; i++) {
GAP_OPEN_PROB_TABLE[i] = logGapOpenProbability;
GAP_CONT_PROB_TABLE[i] = logGapContinuationProbability;
GAP_OPEN_PROB_TABLE[i] = gop;
GAP_CONT_PROB_TABLE[i] = gcp;
}
double gop = logGapOpenProbability;
double gcp = logGapContinuationProbability;
double step = GAP_PENALTY_HRUN_STEP/10.0;
double maxGOP = -MIN_GAP_OPEN_PENALTY/10.0; // phred to log prob
@ -185,60 +187,57 @@ public class PairHMMIndelErrorModel {
}
private double computeReadLikelihoodGivenHaplotypeAffineGaps(byte[] haplotypeBases, byte[] readBases, byte[] readQuals,
double[] currentGOP, double[] currentGCP, int eventLength) {
double[] currentGOP, double[] currentGCP, int indToStart,
double[][] matchMetricArray, double[][] XMetricArray, double[][] YMetricArray) {
final int X_METRIC_LENGTH = readBases.length+1;
final int Y_METRIC_LENGTH = haplotypeBases.length+1;
// initialize path metric and traceback memories for likelihood computation
final double[][] matchMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
final double[][] XMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
final double[][] YMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
if (indToStart == 0) {
// default initialization for all arrays
final double DIAG_TOL = 20; // means that max - min element in diags have to be > this number for banding to take effect.
// default initialization for all arrays
for (int i=0; i < X_METRIC_LENGTH; i++) {
Arrays.fill(matchMetricArray[i],Double.NEGATIVE_INFINITY);
Arrays.fill(YMetricArray[i],Double.NEGATIVE_INFINITY);
Arrays.fill(XMetricArray[i],Double.NEGATIVE_INFINITY);
}
for (int i=1; i < X_METRIC_LENGTH; i++) {
//initialize first column
XMetricArray[i][0] = END_GAP_COST*(i);
}
for (int j=1; j < Y_METRIC_LENGTH; j++) {
// initialize first row
YMetricArray[0][j] = END_GAP_COST*(j);
}
matchMetricArray[0][0]= END_GAP_COST;//Double.NEGATIVE_INFINITY;
XMetricArray[0][0]= YMetricArray[0][0] = 0;
final int numDiags = X_METRIC_LENGTH + Y_METRIC_LENGTH -1;
final int elemsInDiag = Math.min(X_METRIC_LENGTH, Y_METRIC_LENGTH);
int idxWithMaxElement = 0;
double maxElementInDiag = Double.NEGATIVE_INFINITY;
for (int diag=0; diag < numDiags; diag++) {
// compute default I and J start positions at edge of diagonals
int indI = 0;
int indJ = diag;
if (diag >= Y_METRIC_LENGTH ) {
indI = diag-(Y_METRIC_LENGTH-1);
indJ = Y_METRIC_LENGTH-1;
for (int i=0; i < X_METRIC_LENGTH; i++) {
Arrays.fill(matchMetricArray[i],Double.NEGATIVE_INFINITY);
Arrays.fill(YMetricArray[i],Double.NEGATIVE_INFINITY);
Arrays.fill(XMetricArray[i],Double.NEGATIVE_INFINITY);
}
// first pass: from max element to edge
int idxLow = bandedLikelihoods? idxWithMaxElement : 0;
for (int i=1; i < X_METRIC_LENGTH; i++) {
//initialize first column
XMetricArray[i][0] = END_GAP_COST*(i);
}
// reset diag max value before starting
if (bandedLikelihoods) {
maxElementInDiag = Double.NEGATIVE_INFINITY;
for (int j=1; j < Y_METRIC_LENGTH; j++) {
// initialize first row
YMetricArray[0][j] = END_GAP_COST*(j);
}
matchMetricArray[0][0]= END_GAP_COST;//Double.NEGATIVE_INFINITY;
XMetricArray[0][0]= YMetricArray[0][0] = 0;
}
if (bandedLikelihoods) {
final double DIAG_TOL = 20; // means that max - min element in diags have to be > this number for banding to take effect.
final int numDiags = X_METRIC_LENGTH + Y_METRIC_LENGTH -1;
final int elemsInDiag = Math.min(X_METRIC_LENGTH, Y_METRIC_LENGTH);
int idxWithMaxElement = 0;
for (int diag=indToStart; diag < numDiags; diag++) {
// compute default I and J start positions at edge of diagonals
int indI = 0;
int indJ = diag;
if (diag >= Y_METRIC_LENGTH ) {
indI = diag-(Y_METRIC_LENGTH-1);
indJ = Y_METRIC_LENGTH-1;
}
// first pass: from max element to edge
int idxLow = idxWithMaxElement;
// reset diag max value before starting
double maxElementInDiag = Double.NEGATIVE_INFINITY;
// set indI, indJ to correct values
indI += idxLow;
indJ -= idxLow;
@ -248,46 +247,10 @@ public class PairHMMIndelErrorModel {
indJ++;
}
}
for (int el = idxLow; el < elemsInDiag; el++) {
updateCell(indI, indJ, X_METRIC_LENGTH, Y_METRIC_LENGTH, readBases, readQuals, haplotypeBases,
currentGOP, currentGCP, matchMetricArray, XMetricArray, YMetricArray);
// update max in diagonal
if (bandedLikelihoods) {
final double bestMetric = MathUtils.max(matchMetricArray[indI][indJ], XMetricArray[indI][indJ], YMetricArray[indI][indJ]);
// check if we've fallen off diagonal value by threshold
if (bestMetric > maxElementInDiag) {
maxElementInDiag = bestMetric;
idxWithMaxElement = el;
}
else if (bestMetric < maxElementInDiag - DIAG_TOL)
break; // done w/current diagonal
}
indI++;
if (indI >=X_METRIC_LENGTH )
break;
indJ--;
if (indJ <= 0)
break;
}
if (bandedLikelihoods && idxLow > 0) {
// now do second part in opposite direction
indI = 0;
indJ = diag;
if (diag >= Y_METRIC_LENGTH ) {
indI = diag-(Y_METRIC_LENGTH-1);
indJ = Y_METRIC_LENGTH-1;
}
indI += idxLow-1;
indJ -= idxLow-1;
for (int el = idxLow-1; el >= 0; el--) {
for (int el = idxLow; el < elemsInDiag; el++) {
updateCell(indI, indJ, X_METRIC_LENGTH, Y_METRIC_LENGTH, readBases, readQuals, haplotypeBases,
currentGOP, currentGCP, matchMetricArray, XMetricArray, YMetricArray);
currentGOP, currentGCP, matchMetricArray, XMetricArray, YMetricArray);
// update max in diagonal
final double bestMetric = MathUtils.max(matchMetricArray[indI][indJ], XMetricArray[indI][indJ], YMetricArray[indI][indJ]);
@ -296,34 +259,81 @@ public class PairHMMIndelErrorModel {
maxElementInDiag = bestMetric;
idxWithMaxElement = el;
}
else if (bestMetric < maxElementInDiag - DIAG_TOL)
else if (bestMetric < maxElementInDiag - DIAG_TOL && idxWithMaxElement > 0)
break; // done w/current diagonal
indJ++;
if (indJ >= Y_METRIC_LENGTH )
indI++;
if (indI >=X_METRIC_LENGTH )
break;
indI--;
if (indI <= 0)
indJ--;
if (indJ <= 0)
break;
}
if (idxLow > 0) {
// now do second part in opposite direction
indI = 0;
indJ = diag;
if (diag >= Y_METRIC_LENGTH ) {
indI = diag-(Y_METRIC_LENGTH-1);
indJ = Y_METRIC_LENGTH-1;
}
indI += idxLow-1;
indJ -= idxLow-1;
for (int el = idxLow-1; el >= 0; el--) {
updateCell(indI, indJ, X_METRIC_LENGTH, Y_METRIC_LENGTH, readBases, readQuals, haplotypeBases,
currentGOP, currentGCP, matchMetricArray, XMetricArray, YMetricArray);
// update max in diagonal
final double bestMetric = MathUtils.max(matchMetricArray[indI][indJ], XMetricArray[indI][indJ], YMetricArray[indI][indJ]);
// check if we've fallen off diagonal value by threshold
if (bestMetric > maxElementInDiag) {
maxElementInDiag = bestMetric;
idxWithMaxElement = el;
}
else if (bestMetric < maxElementInDiag - DIAG_TOL)
break; // done w/current diagonal
indJ++;
if (indJ >= Y_METRIC_LENGTH )
break;
indI--;
if (indI <= 0)
break;
}
}
// if (DEBUG)
// System.out.format("Max:%4.1f el:%d\n",maxElementInDiag, idxWithMaxElement);
}
// if (DEBUG)
// System.out.format("Max:%4.1f el:%d\n",maxElementInDiag, idxWithMaxElement);
}
else {
// simplified rectangular version of update loop
for (int indI=1; indI < X_METRIC_LENGTH; indI++) {
for (int indJ=indToStart+1; indJ < Y_METRIC_LENGTH; indJ++) {
updateCell(indI, indJ, X_METRIC_LENGTH, Y_METRIC_LENGTH, readBases, readQuals, haplotypeBases,
currentGOP, currentGCP, matchMetricArray, XMetricArray, YMetricArray);
}
}
}
final int bestI = X_METRIC_LENGTH - 1, bestJ = Y_METRIC_LENGTH - 1;
final double bestMetric = MathUtils.softMax(matchMetricArray[bestI][bestJ],
XMetricArray[bestI][bestJ],
YMetricArray[bestI][bestJ]);
/*
/*
if (DEBUG) {
PrintStream outx, outy, outm, outs;
double[][] sumMetrics = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
try {
outx = new PrintStream("../../UGOptim/datax.txt");
outy = new PrintStream("../../UGOptim/datay.txt");
outm = new PrintStream("../../UGOptim/datam.txt");
outs = new PrintStream("../../UGOptim/datas.txt");
outx = new PrintStream("datax.txt");
outy = new PrintStream("datay.txt");
outm = new PrintStream("datam.txt");
outs = new PrintStream("datas.txt");
double metrics[] = new double[3];
for (int indI=0; indI < X_METRIC_LENGTH; indI++) {
for (int indJ=0; indJ < Y_METRIC_LENGTH; indJ++) {
@ -414,8 +424,6 @@ public class PairHMMIndelErrorModel {
continue;
}
double[] recalQuals = null;
// get bases of candidate haplotypes that overlap with reads
final int trailingBases = 3;
@ -534,6 +542,12 @@ public class PairHMMIndelErrorModel {
unclippedReadBases.length-numEndClippedBases);
int j=0;
// initialize path metric and traceback memories for likelihood computation
double[][] matchMetricArray = null, XMetricArray = null, YMetricArray = null;
byte[] previousHaplotypeSeen = null;
double[] previousGOP = null;
int startIdx;
for (Allele a: haplotypeMap.keySet()) {
@ -551,11 +565,37 @@ public class PairHMMIndelErrorModel {
byte[] haplotypeBases = Arrays.copyOfRange(haplotype.getBasesAsBytes(),
(int)indStart, (int)indStop);
double readLikelihood;
if (matchMetricArray == null) {
final int X_METRIC_LENGTH = readBases.length+1;
final int Y_METRIC_LENGTH = haplotypeBases.length+1;
matchMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
XMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
YMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH];
}
final double[] currentContextGOP = Arrays.copyOfRange(gapOpenProbabilityMap.get(a), (int)indStart, (int)indStop);
final double[] currentContextGCP = Arrays.copyOfRange(gapContProbabilityMap.get(a), (int)indStart, (int)indStop);
final double readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals,
currentContextGOP, currentContextGCP, eventLength);
if (previousHaplotypeSeen == null)
startIdx = 0;
else {
int s1 = computeFirstDifferingPosition(haplotypeBases, previousHaplotypeSeen);
int s2 = computeFirstDifferingPosition(currentContextGOP, previousGOP);
startIdx = Math.min(s1,s2);
}
previousHaplotypeSeen = haplotypeBases.clone();
previousGOP = currentContextGOP.clone();
readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals,
currentContextGOP, currentContextGCP, startIdx, matchMetricArray, XMetricArray, YMetricArray);
if (DEBUG) {
System.out.println("H:"+new String(haplotypeBases));
System.out.println("R:"+new String(readBases));
System.out.format("L:%4.2f\n",readLikelihood);
System.out.format("StPos:%d\n", startIdx);
}
readEl.put(a,readLikelihood);
readLikelihoods[readIdx][j++] = readLikelihood;
}
@ -579,6 +619,28 @@ public class PairHMMIndelErrorModel {
return getHaplotypeLikelihoods(numHaplotypes, readCounts, readLikelihoods);
}
private int computeFirstDifferingPosition(byte[] b1, byte[] b2) {
if (b1.length != b2.length)
return 0; // sanity check
for (int i=0; i < b1.length; i++ ){
if ( b1[i]!= b2[i])
return i;
}
return 0; // sanity check
}
private int computeFirstDifferingPosition(double[] b1, double[] b2) {
if (b1.length != b2.length)
return 0; // sanity check
for (int i=0; i < b1.length; i++ ){
if ( b1[i]!= b2[i])
return i;
}
return 0; // sanity check
}
private final static double[] getHaplotypeLikelihoods(final int numHaplotypes, final int readCounts[], final double readLikelihoods[][]) {
final double[][] haplotypeLikehoodMatrix = new double[numHaplotypes][numHaplotypes];

View File

@ -0,0 +1,52 @@
package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications;
import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker;
import org.broadinstitute.sting.utils.MathUtils;
import org.broadinstitute.sting.utils.variantcontext.VariantContext;
import java.util.ArrayList;
import java.util.List;
/**
* Stratifies the eval RODs by the indel size
*
* Indel sizes are stratified from sizes -100 to +100. Sizes greater than this are lumped in the +/- 100 bin
* This stratification ignores multi-allelic indels (whose size is not defined uniquely)
*/
public class IndelSize extends VariantStratifier {
static final int MAX_INDEL_SIZE = 100;
@Override
public void initialize() {
states = new ArrayList<String>();
for( int a=-MAX_INDEL_SIZE; a <=MAX_INDEL_SIZE; a++ ) {
states.add(String.format("%d", a));
}
}
public List<String> getRelevantStates(ReferenceContext ref, RefMetaDataTracker tracker, VariantContext comp, String compName, VariantContext eval, String evalName, String sampleName) {
ArrayList<String> relevantStates = new ArrayList<String>();
if (eval != null && eval.isIndel() && eval.isBiallelic()) {
try {
int eventLength = 0;
if ( eval.isSimpleInsertion() ) {
eventLength = eval.getAlternateAllele(0).length();
} else if ( eval.isSimpleDeletion() ) {
eventLength = -eval.getReference().length();
}
if (eventLength > MAX_INDEL_SIZE)
eventLength = MAX_INDEL_SIZE;
else if (eventLength < -MAX_INDEL_SIZE)
eventLength = -MAX_INDEL_SIZE;
relevantStates.add(String.format("%d",eventLength));
} catch (Exception e) {
return relevantStates;
}
}
return relevantStates;
}
}

View File

@ -25,35 +25,35 @@
package org.broadinstitute.sting.utils.R;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.broadinstitute.sting.commandline.Advanced;
import org.broadinstitute.sting.commandline.Argument;
import org.broadinstitute.sting.commandline.ArgumentCollection;
import org.broadinstitute.sting.gatk.walkers.recalibration.Covariate;
import org.broadinstitute.sting.utils.PathUtils;
import org.broadinstitute.sting.utils.Utils;
import org.broadinstitute.sting.utils.exceptions.StingException;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.io.IOUtils;
import org.broadinstitute.sting.utils.io.Resource;
import org.broadinstitute.sting.utils.runtime.ProcessController;
import org.broadinstitute.sting.utils.runtime.ProcessSettings;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Generic service for executing RScripts in the GATK directory
*
* @author Your Name
* @since Date created
* Generic service for executing RScripts
*/
public class RScriptExecutor {
/**
* our log
*/
protected static Logger logger = Logger.getLogger(RScriptExecutor.class);
private static Logger logger = Logger.getLogger(RScriptExecutor.class);
public static class RScriptArgumentCollection {
@Advanced
@Argument(fullName = "path_to_Rscript", shortName = "Rscript", doc = "The path to your implementation of Rscript. For Broad users this is maybe /broad/software/free/Linux/redhat_5_x86_64/pkgs/r_2.12.0/bin/Rscript", required = false)
@Argument(fullName = "path_to_Rscript", shortName = "Rscript", doc = "The path to your implementation of Rscript. Defaults Rscript meaning to use the first available on the environment PATH. For Broad users should 'use R-2.12' or later.", required = false)
public String PATH_TO_RSCRIPT = "Rscript";
@Advanced
@ -62,40 +62,119 @@ public class RScriptExecutor {
public RScriptArgumentCollection() {}
/** For testing and convenience */
/* For testing and convenience */
public RScriptArgumentCollection(final String PATH_TO_RSCRIPT, final List<String> PATH_TO_RESOURCES) {
this.PATH_TO_RSCRIPT = PATH_TO_RSCRIPT;
this.PATH_TO_RESOURCES = PATH_TO_RESOURCES;
}
}
final RScriptArgumentCollection myArgs;
final boolean exceptOnError;
private final RScriptArgumentCollection myArgs;
private final boolean exceptOnError;
private final List<RScriptLibrary> libraries = new ArrayList<RScriptLibrary>();
private final List<Resource> scriptResources = new ArrayList<Resource>();
private final List<File> scriptFiles = new ArrayList<File>();
private final List<String> args = new ArrayList<String>();
public RScriptExecutor(final RScriptArgumentCollection myArgs, final boolean exceptOnError) {
this.myArgs = myArgs;
this.exceptOnError = exceptOnError;
}
public void callRScripts(String scriptName, Object... scriptArgs) {
callRScripts(scriptName, Arrays.asList(scriptArgs));
public void addLibrary(RScriptLibrary library) {
this.libraries.add(library);
}
public void callRScripts(String scriptName, List<Object> scriptArgs) {
public void addScript(Resource script) {
this.scriptResources.add(script);
}
public void addScript(File script) {
this.scriptFiles.add(script);
}
/**
* Adds args to the end of the Rscript command line.
* @param args the args.
* @throws NullPointerException if any of the args are null.
*/
public void addArgs(Object... args) {
for (Object arg: args)
this.args.add(arg.toString());
}
public void exec() {
List<File> tempFiles = new ArrayList<File>();
try {
final File pathToScript = findScript(scriptName);
if ( pathToScript == null ) return; // we failed but shouldn't exception out
final String argString = Utils.join(" ", scriptArgs);
final String cmdLine = Utils.join(" ", Arrays.asList(myArgs.PATH_TO_RSCRIPT, pathToScript, argString));
logger.info("Executing RScript: " + cmdLine);
Runtime.getRuntime().exec(cmdLine).waitFor();
} catch (InterruptedException e) {
File tempLibDir = IOUtils.tempDir("R.", ".lib");
tempFiles.add(tempLibDir);
StringBuilder expression = new StringBuilder("tempLibDir = '").append(tempLibDir).append("';");
if (this.libraries.size() > 0) {
List<String> tempLibraryPaths = new ArrayList<String>();
for (RScriptLibrary library: this.libraries) {
File tempLibrary = library.writeTemp();
tempFiles.add(tempLibrary);
tempLibraryPaths.add(tempLibrary.getAbsolutePath());
}
expression.append("install.packages(");
expression.append("pkgs=c('").append(StringUtils.join(tempLibraryPaths, "', '")).append("'), lib=tempLibDir, repos=NULL, type='source', ");
// Install faster by eliminating cruft.
expression.append("INSTALL_opts=c('--no-libs', '--no-data', '--no-help', '--no-demo', '--no-exec')");
expression.append(");");
for (RScriptLibrary library: this.libraries) {
expression.append("require('").append(library.getLibraryName()).append("', lib.loc=tempLibDir);");
}
}
for (Resource script: this.scriptResources) {
File tempScript = IOUtils.writeTempResource(script);
tempFiles.add(tempScript);
expression.append("source('").append(tempScript.getAbsolutePath()).append("');");
}
for (File script: this.scriptFiles) {
expression.append("source('").append(script.getAbsolutePath()).append("');");
}
String[] cmd = new String[this.args.size() + 3];
int i = 0;
cmd[i++] = myArgs.PATH_TO_RSCRIPT;
cmd[i++] = "-e";
cmd[i++] = expression.toString();
for (String arg: this.args)
cmd[i++] = arg;
ProcessSettings processSettings = new ProcessSettings(cmd);
if (logger.isDebugEnabled()) {
processSettings.getStdoutSettings().printStandard(true);
processSettings.getStderrSettings().printStandard(true);
}
ProcessController controller = ProcessController.getThreadLocal();
logger.debug("Executing: " + Utils.join(" ", cmd));
logger.debug("Result: " + controller.exec(processSettings).getExitValue());
} catch (StingException e) {
generateException(e);
} catch (IOException e) {
generateException("Fatal Exception: Perhaps RScript jobs are being spawned too quickly?", e);
} finally {
for (File temp: tempFiles)
FileUtils.deleteQuietly(temp);
}
}
public void callRScripts(String scriptName, Object... scriptArgs) {
final File pathToScript = findScript(scriptName);
if (pathToScript == null) return; // we failed but shouldn't exception out
addScript(pathToScript);
addArgs(scriptArgs);
exec();
}
public File findScript(final String scriptName) {
for ( String pathToResource : myArgs.PATH_TO_RESOURCES ) {
final File f = new File(pathToResource + "/" + scriptName);

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.R;
import org.broadinstitute.sting.utils.io.IOUtils;
import org.broadinstitute.sting.utils.io.Resource;
import java.io.File;
/**
* Libraries embedded in the StingUtils package.
*/
public enum RScriptLibrary {
GSALIB("gsalib");
private final String name;
private RScriptLibrary(String name) {
this.name = name;
}
public String getLibraryName() {
return this.name;
}
public String getResourcePath() {
return name + ".tar.gz";
}
/**
* Writes the library source code to a temporary tar.gz file and returns the path.
* @return The path to the library source code. The caller must delete the code when done.
*/
public File writeTemp() {
return IOUtils.writeTempResource(new Resource(getResourcePath(), RScriptLibrary.class));
}
}

View File

@ -29,6 +29,7 @@ import org.apache.log4j.Logger;
import org.broadinstitute.sting.commandline.ArgumentDefinition;
import org.broadinstitute.sting.commandline.ArgumentDefinitionGroup;
import org.broadinstitute.sting.commandline.ArgumentDefinitions;
import org.broadinstitute.sting.commandline.ArgumentMatchSource;
import org.broadinstitute.sting.utils.Utils;
import org.broadinstitute.sting.utils.text.TextFormattingUtils;
@ -47,6 +48,7 @@ public class HelpFormatter {
/**
* Prints the help, given a collection of argument definitions.
* @param applicationDetails Application details
* @param argumentDefinitions Argument definitions for which help should be printed.
*/
public void printHelp( ApplicationDetails applicationDetails, ArgumentDefinitions argumentDefinitions ) {
@ -233,7 +235,7 @@ public class HelpFormatter {
private List<ArgumentDefinitionGroup> prepareArgumentGroups( ArgumentDefinitions argumentDefinitions ) {
// Sort the list of argument definitions according to how they should be shown.
// Put the sorted results into a new cloned data structure.
Comparator definitionComparator = new Comparator<ArgumentDefinition>() {
Comparator<ArgumentDefinition> definitionComparator = new Comparator<ArgumentDefinition>() {
public int compare( ArgumentDefinition lhs, ArgumentDefinition rhs ) {
if( lhs.required && rhs.required ) return 0;
if( lhs.required ) return -1;
@ -242,15 +244,15 @@ public class HelpFormatter {
}
};
List<ArgumentDefinitionGroup> argumentGroups = new ArrayList();
List<ArgumentDefinitionGroup> argumentGroups = new ArrayList<ArgumentDefinitionGroup>();
for( ArgumentDefinitionGroup argumentGroup: argumentDefinitions.getArgumentDefinitionGroups() ) {
List<ArgumentDefinition> sortedDefinitions = new ArrayList( argumentGroup.argumentDefinitions );
List<ArgumentDefinition> sortedDefinitions = new ArrayList<ArgumentDefinition>( argumentGroup.argumentDefinitions );
Collections.sort( sortedDefinitions, definitionComparator );
argumentGroups.add( new ArgumentDefinitionGroup(argumentGroup.groupName,sortedDefinitions) );
}
// Sort the argument groups themselves with main arguments first, followed by plugins sorted in name order.
Comparator groupComparator = new Comparator<ArgumentDefinitionGroup>() {
Comparator<ArgumentDefinitionGroup> groupComparator = new Comparator<ArgumentDefinitionGroup>() {
public int compare( ArgumentDefinitionGroup lhs, ArgumentDefinitionGroup rhs ) {
if( lhs.groupName == null && rhs.groupName == null ) return 0;
if( lhs.groupName == null ) return -1;
@ -271,9 +273,9 @@ public class HelpFormatter {
* Generate a standard header for the logger
*
* @param applicationDetails details of the application to run.
* @param args the command line arguments passed in
* @param parsedArgs the command line arguments passed in
*/
public static void generateHeaderInformation(ApplicationDetails applicationDetails, String[] args) {
public static void generateHeaderInformation(ApplicationDetails applicationDetails, Map<ArgumentMatchSource, List<String>> parsedArgs) {
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
java.util.Date date = new java.util.Date();
@ -283,11 +285,22 @@ public class HelpFormatter {
logger.info(barrier);
for (String headerLine : applicationDetails.applicationHeader)
logger.info(headerLine);
String output = "";
for (String str : args) {
output = output + str + " ";
logger.debug("Current directory: " + System.getProperty("user.dir"));
for (Map.Entry<ArgumentMatchSource, List<String>> entry: parsedArgs.entrySet()) {
ArgumentMatchSource matchSource = entry.getKey();
final String sourceName;
switch (matchSource.getType()) {
case CommandLine: sourceName = "Program"; break;
case File: sourceName = matchSource.getFile().getPath(); break;
default: throw new RuntimeException("Unexpected argument match source type: " + matchSource.getType());
}
String output = sourceName + " Args:";
for (String str : entry.getValue()) {
output = output + " " + str;
}
logger.info(output);
}
logger.info("Program Args: " + output);
logger.info("Date/Time: " + dateFormat.format(date));
logger.info(barrier);

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.io;
import java.io.File;
public interface FileExtension {
/**
* Returns a clone of the FileExtension with a new path.
* @param path New path.
* @return New FileExtension
*/
public File withPath(String path);
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.io;
import org.apache.commons.io.output.ThresholdingOutputStream;
import java.io.IOException;
/**
* An output stream which stops at the threshold
* instead of potentially triggering early.
*/
public abstract class HardThresholdingOutputStream extends ThresholdingOutputStream {
protected HardThresholdingOutputStream(int threshold) {
super(threshold);
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
int remaining = this.getThreshold() - (int)this.getByteCount();
if (!isThresholdExceeded() && len > remaining) {
super.write(b, off, remaining);
super.write(b, off + remaining, len - remaining);
} else {
super.write(b, off, len);
}
}
}

View File

@ -0,0 +1,353 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.io;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.broadinstitute.sting.utils.exceptions.StingException;
import org.broadinstitute.sting.utils.exceptions.UserException;
import java.io.*;
import java.util.*;
public class IOUtils {
private static Logger logger = Logger.getLogger(IOUtils.class);
/**
* Checks if the temp directory has been setup and throws an exception if they user hasn't set it correctly.
*
* @param tempDir Temporary directory.
*/
public static void checkTempDir(File tempDir) {
String tempDirPath = tempDir.getAbsolutePath();
// Keeps the user from leaving the temp directory as the default, and on Macs from having pluses
// in the path which can cause problems with the Google Reflections library.
// see also: http://benjchristensen.com/2009/09/22/mac-osx-10-6-java-java-io-tmpdir/
if (tempDirPath.startsWith("/var/folders/") || (tempDirPath.equals("/tmp")) || (tempDirPath.equals("/tmp/")))
throw new UserException.BadTmpDir("java.io.tmpdir must be explicitly set");
if (!tempDir.exists() && !tempDir.mkdirs())
throw new UserException.BadTmpDir("Could not create directory: " + tempDir.getAbsolutePath());
}
/**
* Creates a temp directory with the prefix and optional suffix.
*
* @param prefix Prefix for the directory name.
* @param suffix Optional suffix for the directory name.
* @return The created temporary directory.
*/
public static File tempDir(String prefix, String suffix) {
return tempDir(prefix, suffix, null);
}
/**
* Creates a temp directory with the prefix and optional suffix.
*
* @param prefix Prefix for the directory name.
* @param suffix Optional suffix for the directory name.
* @param tempDirParent Parent directory for the temp directory.
* @return The created temporary directory.
*/
public static File tempDir(String prefix, String suffix, File tempDirParent) {
try {
if (tempDirParent == null)
tempDirParent = FileUtils.getTempDirectory();
if (!tempDirParent.exists() && !tempDirParent.mkdirs())
throw new UserException.BadTmpDir("Could not create temp directory: " + tempDirParent);
File temp = File.createTempFile(prefix + "-", suffix, tempDirParent);
if (!temp.delete())
throw new UserException.BadTmpDir("Could not delete sub file: " + temp.getAbsolutePath());
if (!temp.mkdir())
throw new UserException.BadTmpDir("Could not create sub directory: " + temp.getAbsolutePath());
return absolute(temp);
} catch (IOException e) {
throw new UserException.BadTmpDir(e.getMessage());
}
}
/**
* Writes content to a temp file and returns the path to the temporary file.
*
* @param content to write.
* @param prefix Prefix for the temp file.
* @param suffix Suffix for the temp file.
* @param directory Directory for the temp file.
* @return the path to the temp file.
*/
public static File writeTempFile(String content, String prefix, String suffix, File directory) {
try {
File tempFile = absolute(File.createTempFile(prefix, suffix, directory));
FileUtils.writeStringToFile(tempFile, content);
return tempFile;
} catch (IOException e) {
throw new UserException.BadTmpDir(e.getMessage());
}
}
/**
* Waits for NFS to propagate a file creation, imposing a timeout.
*
* Based on Apache Commons IO FileUtils.waitFor()
*
* @param file The file to wait for.
* @param seconds The maximum time in seconds to wait.
* @return true if the file exists
*/
public static boolean waitFor(File file, int seconds) {
return waitFor(Collections.singletonList(file), seconds).isEmpty();
}
/**
* Waits for NFS to propagate a file creation, imposing a timeout.
*
* Based on Apache Commons IO FileUtils.waitFor()
*
* @param files The list of files to wait for.
* @param seconds The maximum time in seconds to wait.
* @return Files that still do not exists at the end of the timeout, or a empty list if all files exists.
*/
public static List<File> waitFor(Collection<File> files, int seconds) {
long timeout = 0;
long tick = 0;
List<File> missingFiles = new ArrayList<File>();
for (File file : files)
if (!file.exists())
missingFiles.add(file);
while (!missingFiles.isEmpty() && timeout <= seconds) {
if (tick >= 10) {
tick = 0;
timeout++;
}
tick++;
try {
Thread.sleep(100);
} catch (InterruptedException ignore) {
}
List<File> newMissingFiles = new ArrayList<File>();
for (File file : missingFiles)
if (!file.exists())
newMissingFiles.add(file);
missingFiles = newMissingFiles;
}
return missingFiles;
}
/**
* Returns the directory at the number of levels deep.
* For example 2 levels of /path/to/dir will return /path/to
*
* @param dir Directory path.
* @param level how many levels deep from the root.
* @return The path to the parent directory that is level-levels deep.
*/
public static File dirLevel(File dir, int level) {
List<File> directories = new ArrayList<File>();
File parentDir = absolute(dir);
while (parentDir != null) {
directories.add(0, parentDir);
parentDir = parentDir.getParentFile();
}
if (directories.size() <= level)
return directories.get(directories.size() - 1);
else
return directories.get(level);
}
/**
* Returns the sub path rooted at the parent.
*
* @param parent The parent directory.
* @param path The sub path to append to the parent, if the path is not absolute.
* @return The absolute path to the file in the parent dir if the path was not absolute, otherwise the original path.
*/
public static File absolute(File parent, String path) {
return absolute(parent, new File(path));
}
/**
* Returns the sub path rooted at the parent.
*
* @param parent The parent directory.
* @param file The sub path to append to the parent, if the path is not absolute.
* @return The absolute path to the file in the parent dir if the path was not absolute, otherwise the original path.
*/
public static File absolute(File parent, File file) {
String newPath;
if (file.isAbsolute())
newPath = absolutePath(file);
else
newPath = absolutePath(new File(parent, file.getPath()));
return replacePath(file, newPath);
}
/**
* A mix of getCanonicalFile and getAbsoluteFile that returns the
* absolute path to the file without deferencing symbolic links.
*
* @param file the file.
* @return the absolute path to the file.
*/
public static File absolute(File file) {
return replacePath(file, absolutePath(file));
}
private static String absolutePath(File file) {
File fileAbs = file.getAbsoluteFile();
LinkedList<String> names = new LinkedList<String>();
while (fileAbs != null) {
String name = fileAbs.getName();
fileAbs = fileAbs.getParentFile();
if (".".equals(name)) {
/* skip */
/* TODO: What do we do for ".."?
} else if (name == "..") {
CentOS tcsh says use getCanonicalFile:
~ $ mkdir -p test1/test2
~ $ ln -s test1/test2 test3
~ $ cd test3/..
~/test1 $
Mac bash says keep going with getAbsoluteFile:
~ $ mkdir -p test1/test2
~ $ ln -s test1/test2 test3
~ $ cd test3/..
~ $
For now, leave it and let the shell figure it out.
*/
} else {
names.add(0, name);
}
}
return ("/" + StringUtils.join(names, "/"));
}
private static File replacePath(File file, String path) {
if (file instanceof FileExtension)
return ((FileExtension)file).withPath(path);
if (!File.class.equals(file.getClass()))
throw new StingException("Sub classes of java.io.File must also implement FileExtension");
return new File(path);
}
/**
* Returns the last lines of the file.
* NOTE: This is only safe to run on smaller files!
*
* @param file File to read.
* @param count Maximum number of lines to return.
* @return The last count lines from file.
* @throws IOException When unable to read the file.
*/
public static List<String> tail(File file, int count) throws IOException {
LinkedList<String> tailLines = new LinkedList<String>();
FileReader reader = new FileReader(file);
try {
LineIterator iterator = org.apache.commons.io.IOUtils.lineIterator(reader);
int lineCount = 0;
while (iterator.hasNext()) {
String line = iterator.nextLine();
lineCount++;
if (lineCount > count)
tailLines.removeFirst();
tailLines.offer(line);
}
} finally {
org.apache.commons.io.IOUtils.closeQuietly(reader);
}
return tailLines;
}
/**
* Tries to delete a file. Emits a warning if the file was unable to be deleted.
*
* @param file File to delete.
* @return true if the file was deleted.
*/
public static boolean tryDelete(File file) {
boolean deleted = FileUtils.deleteQuietly(file);
if (deleted)
logger.debug("Deleted " + file);
else if (file.exists())
logger.warn("Unable to delete " + file);
return deleted;
}
/**
* Writes the an embedded resource to a temp file.
* File is not scheduled for deletion and must be cleaned up by the caller.
* @param resource Embedded resource.
* @return Path to the temp file with the contents of the resource.
*/
public static File writeTempResource(Resource resource) {
File temp;
try {
temp = File.createTempFile(FilenameUtils.getBaseName(resource.getPath()) + ".", "." + FilenameUtils.getExtension(resource.getPath()));
} catch (IOException e) {
throw new UserException.BadTmpDir(e.getMessage());
}
writeResource(resource, temp);
return temp;
}
/**
* Writes the an embedded resource to a file.
* File is not scheduled for deletion and must be cleaned up by the caller.
* @param resource Embedded resource.
* @param file File path to write.
*/
public static void writeResource(Resource resource, File file) {
String path = resource.getPath();
Class<?> clazz = resource.getRelativeClass();
InputStream inputStream = null;
OutputStream outputStream = null;
try {
if (clazz == null) {
inputStream = ClassLoader.getSystemResourceAsStream(path);
if (inputStream == null)
throw new IllegalArgumentException("Resource not found: " + path);
} else {
inputStream = clazz.getResourceAsStream(path);
if (inputStream == null)
throw new IllegalArgumentException("Resource not found relative to " + clazz + ": " + path);
}
outputStream = FileUtils.openOutputStream(file);
org.apache.commons.io.IOUtils.copy(inputStream, outputStream);
} catch (IOException e) {
throw new StingException(String.format("Unable to copy resource '%s' to '%s'", path, file), e);
} finally {
org.apache.commons.io.IOUtils.closeQuietly(inputStream);
org.apache.commons.io.IOUtils.closeQuietly(outputStream);
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.io;
/**
* Stores a resource by path and a relative class.
*/
public class Resource {
private final String path;
private final Class<?> relativeClass;
/**
* Create a resource with a path and a relative class.
* @param path Relative or absolute path to the class.
* @param relativeClass Relative class to use as a class loader and for a relative package.
*
* If the relative class is null then the system classloader will be used and the path must be absolute.
*/
public Resource(String path, Class<?> relativeClass) {
this.path = path;
this.relativeClass = relativeClass;
}
public Class<?> getRelativeClass() {
return relativeClass;
}
public String getPath() {
return path;
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.io.HardThresholdingOutputStream;
import java.io.*;
import java.util.EnumMap;
/**
* Stream output captured from a stream.
*/
public class CapturedStreamOutput extends StreamOutput {
private final InputStream processStream;
private final EnumMap<StreamLocation, OutputStream> outputStreams = new EnumMap<StreamLocation, OutputStream>(StreamLocation.class);
/**
* The byte stream to capture content or null if no output string content was requested.
*/
private final ByteArrayOutputStream bufferStream;
/**
* True if the buffer is truncated.
*/
private boolean bufferTruncated = false;
/**
* @param settings Settings that define what to capture.
* @param processStream Stream to capture output.
* @param standardStream Stream to write debug output.
*/
public CapturedStreamOutput(OutputStreamSettings settings, InputStream processStream, PrintStream standardStream) {
this.processStream = processStream;
int bufferSize = settings.getBufferSize();
this.bufferStream = (bufferSize < 0) ? new ByteArrayOutputStream() : new ByteArrayOutputStream(bufferSize);
for (StreamLocation location : settings.getStreamLocations()) {
OutputStream outputStream;
switch (location) {
case Buffer:
if (bufferSize < 0) {
outputStream = this.bufferStream;
} else {
outputStream = new HardThresholdingOutputStream(bufferSize) {
@Override
protected OutputStream getStream() throws IOException {
return bufferTruncated ? NullOutputStream.NULL_OUTPUT_STREAM : bufferStream;
}
@Override
protected void thresholdReached() throws IOException {
bufferTruncated = true;
}
};
}
break;
case File:
try {
outputStream = new FileOutputStream(settings.getOutputFile(), settings.isAppendFile());
} catch (IOException e) {
throw new UserException.BadInput(e.getMessage());
}
break;
case Standard:
outputStream = standardStream;
break;
default:
throw new ReviewedStingException("Unexpected stream location: " + location);
}
this.outputStreams.put(location, outputStream);
}
}
@Override
public byte[] getBufferBytes() {
return bufferStream.toByteArray();
}
@Override
public boolean isBufferTruncated() {
return bufferTruncated;
}
/**
* Drain the input stream to keep the process from backing up until it's empty.
* File streams will be closed automatically when this method returns.
*
* @throws java.io.IOException When unable to read or write.
*/
public void readAndClose() throws IOException {
try {
byte[] buf = new byte[4096];
int readCount;
while ((readCount = processStream.read(buf)) >= 0)
for (OutputStream outputStream : this.outputStreams.values()) {
outputStream.write(buf, 0, readCount);
}
} finally {
for (StreamLocation location : this.outputStreams.keySet()) {
OutputStream outputStream = this.outputStreams.get(location);
outputStream.flush();
if (location != StreamLocation.Standard)
IOUtils.closeQuietly(outputStream);
}
}
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
import java.io.File;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
/**
* Settings that define text to write to the process stdin.
*/
public class InputStreamSettings {
private final EnumSet<StreamLocation> streamLocations = EnumSet.noneOf(StreamLocation.class);
private byte[] inputBuffer;
private File inputFile;
public InputStreamSettings() {
}
/**
* @param inputBuffer String to write to stdin.
*/
public InputStreamSettings(String inputBuffer) {
setInputBuffer(inputBuffer);
}
/**
* @param inputFile File to write to stdin.
*/
public InputStreamSettings(File inputFile) {
setInputFile(inputFile);
}
/**
* @param inputBuffer String to write to stdin.
* @param inputFile File to write to stdin.
*/
public InputStreamSettings(byte[] inputBuffer, File inputFile) {
setInputBuffer(inputBuffer);
setInputFile(inputFile);
}
public Set<StreamLocation> getStreamLocations() {
return Collections.unmodifiableSet(streamLocations);
}
public byte[] getInputBuffer() {
return inputBuffer;
}
public void setInputBuffer(String inputBuffer) {
if (inputBuffer == null)
throw new IllegalArgumentException("inputBuffer cannot be null");
this.streamLocations.add(StreamLocation.Buffer);
this.inputBuffer = inputBuffer.getBytes();
}
public void setInputBuffer(byte[] inputBuffer) {
if (inputBuffer == null)
throw new IllegalArgumentException("inputBuffer cannot be null");
this.streamLocations.add(StreamLocation.Buffer);
this.inputBuffer = inputBuffer;
}
public void clearInputBuffer() {
this.streamLocations.remove(StreamLocation.Buffer);
this.inputBuffer = null;
}
public File getInputFile() {
return inputFile;
}
public void setInputFile(File inputFile) {
if (inputFile == null)
throw new IllegalArgumentException("inputFile cannot be null");
this.streamLocations.add(StreamLocation.File);
this.inputFile = inputFile;
}
public void clearInputFile() {
this.streamLocations.remove(StreamLocation.File);
this.inputFile = null;
}
public void setInputStandard(boolean inputStandard) {
if (inputStandard)
this.streamLocations.add(StreamLocation.Standard);
else
this.streamLocations.remove(StreamLocation.Standard);
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
import java.io.File;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
/**
* Settings that define text to capture from a process stream.
*/
public class OutputStreamSettings {
private final EnumSet<StreamLocation> streamLocations = EnumSet.noneOf(StreamLocation.class);
private int bufferSize;
private File outputFile;
private boolean appendFile;
public OutputStreamSettings() {
}
/**
* @param bufferSize The number of bytes to capture, or -1 for unlimited.
*/
public OutputStreamSettings(int bufferSize) {
setBufferSize(bufferSize);
}
/**
* @param outputFile The file to write output to.
*/
public OutputStreamSettings(File outputFile) {
setOutputFile(outputFile);
}
/**
* @param outputFile The file to write output to.
* @param append true if the output file should be appended to.
*/
public OutputStreamSettings(File outputFile, boolean append) {
setOutputFile(outputFile, append);
}
public OutputStreamSettings(int bufferSize, File outputFile, boolean appendFile) {
setBufferSize(bufferSize);
setOutputFile(outputFile, appendFile);
}
public Set<StreamLocation> getStreamLocations() {
return Collections.unmodifiableSet(streamLocations);
}
public int getBufferSize() {
return bufferSize;
}
public void setBufferSize(int bufferSize) {
this.streamLocations.add(StreamLocation.Buffer);
this.bufferSize = bufferSize;
}
public void clearBufferSize() {
this.streamLocations.remove(StreamLocation.Buffer);
this.bufferSize = 0;
}
public File getOutputFile() {
return outputFile;
}
public boolean isAppendFile() {
return appendFile;
}
/**
* Overwrites the outputFile with the process output.
*
* @param outputFile File to overwrite.
*/
public void setOutputFile(File outputFile) {
setOutputFile(outputFile, false);
}
public void setOutputFile(File outputFile, boolean append) {
if (outputFile == null)
throw new IllegalArgumentException("outputFile cannot be null");
streamLocations.add(StreamLocation.File);
this.outputFile = outputFile;
this.appendFile = append;
}
public void clearOutputFile() {
streamLocations.remove(StreamLocation.File);
this.outputFile = null;
this.appendFile = false;
}
public void printStandard(boolean print) {
if (print)
this.streamLocations.add(StreamLocation.Standard);
else
this.streamLocations.remove(StreamLocation.Standard);
}
}

View File

@ -0,0 +1,363 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.broadinstitute.sting.utils.exceptions.UserException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
/**
* Facade to Runtime.exec() and java.lang.Process. Handles
* running a process to completion and returns stdout and stderr
* as strings. Creates separate threads for reading stdout and stderr,
* then reuses those threads for each process most efficient use is
* to create one of these and use it repeatedly. Instances are not
* thread-safe, however.
*
* TODO: java.io sometimes zombies the backround threads locking up on read().
* Supposedly NIO has better ways of interrupting a blocked stream but will
* require a little bit of refactoring.
*
* @author Michael Koehrsen
* @author Khalid Shakir
*/
public class ProcessController {
private static Logger logger = Logger.getLogger(ProcessController.class);
private static enum ProcessStream {Stdout, Stderr}
// Tracks running processes.
private static final Set<ProcessController> running = Collections.synchronizedSet(new HashSet<ProcessController>());
// Tracks this running process.
private Process process;
// Threads that capture stdout and stderr
private final OutputCapture stdoutCapture;
private final OutputCapture stderrCapture;
// When a caller destroyes a controller a new thread local version will be created
private boolean destroyed = false;
// Communication channels with output capture threads
// Holds the stdout and stderr sent to the background capture threads
private final Map<ProcessStream, CapturedStreamOutput> toCapture =
new EnumMap<ProcessStream, CapturedStreamOutput>(ProcessStream.class);
// Holds the results of the capture from the background capture threads.
// May be the content via toCapture or an StreamOutput.EMPTY if the capture was interrupted.
private final Map<ProcessStream, StreamOutput> fromCapture =
new EnumMap<ProcessStream, StreamOutput>(ProcessStream.class);
// Useful for debugging if background threads have shut down correctly
private static int nextControllerId = 0;
private final int controllerId;
public ProcessController() {
// Start the background threads for this controller.
synchronized (running) {
controllerId = nextControllerId++;
}
stdoutCapture = new OutputCapture(ProcessStream.Stdout, controllerId);
stderrCapture = new OutputCapture(ProcessStream.Stderr, controllerId);
stdoutCapture.start();
stderrCapture.start();
}
/**
* Returns a thread local ProcessController.
* Should NOT be closed when finished so it can be reused by the thread.
*
* @return a thread local ProcessController.
*/
public static ProcessController getThreadLocal() {
// If the local controller was destroyed get a fresh instance.
if (threadProcessController.get().destroyed)
threadProcessController.remove();
return threadProcessController.get();
}
/**
* Thread local process controller container.
*/
private static final ThreadLocal<ProcessController> threadProcessController =
new ThreadLocal<ProcessController>() {
@Override
protected ProcessController initialValue() {
return new ProcessController();
}
};
/**
* Similar to Runtime.exec() but drains the output and error streams.
*
* @param command Command to run.
* @return The result code.
*/
public static int exec(String[] command) {
ProcessController controller = ProcessController.getThreadLocal();
return controller.exec(new ProcessSettings(command)).getExitValue();
}
/**
* Executes a command line program with the settings and waits for it to return,
* processing the output on a background thread.
*
* @param settings Settings to be run.
* @return The output of the command.
*/
public ProcessOutput exec(ProcessSettings settings) {
if (destroyed)
throw new IllegalStateException("This controller was destroyed");
ProcessBuilder builder = new ProcessBuilder(settings.getCommand());
builder.directory(settings.getDirectory());
Map<String, String> settingsEnvironment = settings.getEnvironment();
if (settingsEnvironment != null) {
Map<String, String> builderEnvironment = builder.environment();
builderEnvironment.clear();
builderEnvironment.putAll(settingsEnvironment);
}
builder.redirectErrorStream(settings.isRedirectErrorStream());
StreamOutput stdout = null;
StreamOutput stderr = null;
// Start the process running.
try {
synchronized (toCapture) {
process = builder.start();
}
running.add(this);
} catch (IOException e) {
throw new ReviewedStingException("Unable to start command: " + StringUtils.join(builder.command(), " "));
}
int exitCode;
try {
// Notify the background threads to start capturing.
synchronized (toCapture) {
toCapture.put(ProcessStream.Stdout,
new CapturedStreamOutput(settings.getStdoutSettings(), process.getInputStream(), System.out));
toCapture.put(ProcessStream.Stderr,
new CapturedStreamOutput(settings.getStderrSettings(), process.getErrorStream(), System.err));
toCapture.notifyAll();
}
// Write stdin content
InputStreamSettings stdinSettings = settings.getStdinSettings();
Set<StreamLocation> streamLocations = stdinSettings.getStreamLocations();
if (!streamLocations.isEmpty()) {
try {
OutputStream stdinStream = process.getOutputStream();
for (StreamLocation location : streamLocations) {
InputStream inputStream;
switch (location) {
case Buffer:
inputStream = new ByteArrayInputStream(stdinSettings.getInputBuffer());
break;
case File:
try {
inputStream = FileUtils.openInputStream(stdinSettings.getInputFile());
} catch (IOException e) {
throw new UserException.BadInput(e.getMessage());
}
break;
case Standard:
inputStream = System.in;
break;
default:
throw new ReviewedStingException("Unexpected stream location: " + location);
}
try {
IOUtils.copy(inputStream, stdinStream);
} finally {
if (location != StreamLocation.Standard)
IOUtils.closeQuietly(inputStream);
}
}
stdinStream.flush();
} catch (IOException e) {
throw new ReviewedStingException("Error writing to stdin on command: " + StringUtils.join(builder.command(), " "), e);
}
}
// Wait for the process to complete.
try {
process.getOutputStream().close();
process.waitFor();
} catch (IOException e) {
throw new ReviewedStingException("Unable to close stdin on command: " + StringUtils.join(builder.command(), " "), e);
} catch (InterruptedException e) {
throw new ReviewedStingException("Process interrupted", e);
} finally {
while (!destroyed && stdout == null || stderr == null) {
synchronized (fromCapture) {
if (fromCapture.containsKey(ProcessStream.Stdout))
stdout = fromCapture.remove(ProcessStream.Stdout);
if (fromCapture.containsKey(ProcessStream.Stderr))
stderr = fromCapture.remove(ProcessStream.Stderr);
try {
if (stdout == null || stderr == null)
fromCapture.wait();
} catch (InterruptedException e) {
// Log the error, ignore the interrupt and wait patiently
// for the OutputCaptures to (via finally) return their
// stdout and stderr.
logger.error(e);
}
}
}
if (destroyed) {
if (stdout == null)
stdout = StreamOutput.EMPTY;
if (stderr == null)
stderr = StreamOutput.EMPTY;
}
}
} finally {
synchronized (toCapture) {
exitCode = process.exitValue();
process = null;
}
running.remove(this);
}
return new ProcessOutput(exitCode, stdout, stderr);
}
/**
* @return The set of still running processes.
*/
public static Set<ProcessController> getRunning() {
synchronized (running) {
return new HashSet<ProcessController>(running);
}
}
/**
* Stops the process from running and tries to ensure process is cleaned up properly.
* NOTE: sub-processes started by process may be zombied with their parents set to pid 1.
* NOTE: capture threads may block on read.
* TODO: Try to use NIO to interrupt streams.
*/
public void tryDestroy() {
destroyed = true;
synchronized (toCapture) {
if (process != null) {
process.destroy();
IOUtils.closeQuietly(process.getInputStream());
IOUtils.closeQuietly(process.getErrorStream());
}
stdoutCapture.interrupt();
stderrCapture.interrupt();
toCapture.notifyAll();
}
}
@Override
protected void finalize() throws Throwable {
try {
tryDestroy();
} catch (Exception e) {
logger.error(e);
}
super.finalize();
}
private class OutputCapture extends Thread {
private final int controllerId;
private final ProcessStream key;
/**
* Reads in the output of a stream on a background thread to keep the output pipe from backing up and freezing the called process.
*
* @param key The stdout or stderr key for this output capture.
* @param controllerId Unique id of the controller.
*/
public OutputCapture(ProcessStream key, int controllerId) {
super(String.format("OutputCapture-%d-%s-%s-%d", controllerId, key.name().toLowerCase(),
Thread.currentThread().getName(), Thread.currentThread().getId()));
this.controllerId = controllerId;
this.key = key;
setDaemon(true);
}
/**
* Runs the capture.
*/
@Override
public void run() {
while (!destroyed) {
StreamOutput processStream = StreamOutput.EMPTY;
try {
// Wait for a new input stream to be passed from this process controller.
CapturedStreamOutput capturedProcessStream = null;
while (!destroyed && capturedProcessStream == null) {
synchronized (toCapture) {
if (toCapture.containsKey(key)) {
capturedProcessStream = toCapture.remove(key);
} else {
toCapture.wait();
}
}
}
if (!destroyed) {
// Read in the input stream
processStream = capturedProcessStream;
capturedProcessStream.readAndClose();
}
} catch (InterruptedException e) {
logger.info("OutputCapture interrupted, exiting");
break;
} catch (IOException e) {
logger.error("Error reading process output", e);
} finally {
// Send the string back to the process controller.
synchronized (fromCapture) {
fromCapture.put(key, processStream);
fromCapture.notify();
}
}
}
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
public class ProcessOutput {
private final int exitValue;
private final StreamOutput stdout;
private final StreamOutput stderr;
/**
* The output of a process.
*
* @param exitValue The exit value.
* @param stdout The capture of stdout as defined by the stdout OutputStreamSettings.
* @param stderr The capture of stderr as defined by the stderr OutputStreamSettings.
*/
public ProcessOutput(int exitValue, StreamOutput stdout, StreamOutput stderr) {
this.exitValue = exitValue;
this.stdout = stdout;
this.stderr = stderr;
}
public int getExitValue() {
return exitValue;
}
public StreamOutput getStdout() {
return stdout;
}
public StreamOutput getStderr() {
return stderr;
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
import com.sun.corba.se.spi.orbutil.fsm.Input;
import java.io.File;
import java.util.Map;
public class ProcessSettings {
private String[] command;
private Map<String, String> environment;
private File directory;
private boolean redirectErrorStream;
private InputStreamSettings stdinSettings;
private OutputStreamSettings stdoutSettings;
private OutputStreamSettings stderrSettings;
/**
* @param command Command line to run.
*/
public ProcessSettings(String[] command) {
this(command, false, null, null, null, null, null);
}
/**
* @param command Command line to run.
* @param redirectErrorStream true if stderr should be sent to stdout.
* @param environment Environment settings to override System.getEnv, or null to use System.getEnv.
* @param directory The directory to run the command in, or null to run in the current directory.
* @param stdinSettings Settings for writing to the process stdin.
* @param stdoutSettings Settings for capturing the process stdout.
* @param stderrSettings Setting for capturing the process stderr.
*/
public ProcessSettings(String[] command, boolean redirectErrorStream, File directory, Map<String, String> environment,
InputStreamSettings stdinSettings, OutputStreamSettings stdoutSettings, OutputStreamSettings stderrSettings) {
this.command = checkCommand(command);
this.redirectErrorStream = redirectErrorStream;
this.directory = directory;
this.environment = environment;
this.stdinSettings = checkSettings(stdinSettings);
this.stdoutSettings = checkSettings(stdoutSettings);
this.stderrSettings = checkSettings(stderrSettings);
}
public String[] getCommand() {
return command;
}
public void setCommand(String[] command) {
this.command = checkCommand(command);
}
public boolean isRedirectErrorStream() {
return redirectErrorStream;
}
public void setRedirectErrorStream(boolean redirectErrorStream) {
this.redirectErrorStream = redirectErrorStream;
}
public File getDirectory() {
return directory;
}
public void setDirectory(File directory) {
this.directory = directory;
}
public Map<String, String> getEnvironment() {
return environment;
}
public void setEnvironment(Map<String, String> environment) {
this.environment = environment;
}
public InputStreamSettings getStdinSettings() {
return stdinSettings;
}
public void setStdinSettings(InputStreamSettings stdinSettings) {
this.stdinSettings = checkSettings(stdinSettings);
}
public OutputStreamSettings getStdoutSettings() {
return stdoutSettings;
}
public void setStdoutSettings(OutputStreamSettings stdoutSettings) {
this.stdoutSettings = checkSettings(stdoutSettings);
}
public OutputStreamSettings getStderrSettings() {
return stderrSettings;
}
public void setStderrSettings(OutputStreamSettings stderrSettings) {
this.stderrSettings = checkSettings(stderrSettings);
}
protected String[] checkCommand(String[] command) {
if (command == null)
throw new IllegalArgumentException("Command is not allowed to be null");
for (String s: command)
if (s == null)
throw new IllegalArgumentException("Command is not allowed to contain nulls");
return command;
}
protected InputStreamSettings checkSettings(InputStreamSettings settings) {
return settings == null ? new InputStreamSettings() : settings;
}
protected OutputStreamSettings checkSettings(OutputStreamSettings settings) {
return settings == null ? new OutputStreamSettings() : settings;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
/**
* Where to read/write a stream
*/
public enum StreamLocation {
Buffer, File, Standard
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
/**
* The content of stdout or stderr.
*/
public abstract class StreamOutput {
/**
* Empty stream output when no output is captured due to an error.
*/
public static final StreamOutput EMPTY = new StreamOutput() {
@Override
public byte[] getBufferBytes() {
return new byte[0];
}
@Override
public boolean isBufferTruncated() {
return false;
}
};
/**
* Returns the content as a string.
*
* @return The content as a string.
*/
public String getBufferString() {
return new String(getBufferBytes());
}
/**
* Returns the content as a string.
*
* @return The content as a string.
*/
public abstract byte[] getBufferBytes();
/**
* Returns true if the buffer was truncated.
*
* @return true if the buffer was truncated.
*/
public abstract boolean isBufferTruncated();
}

View File

@ -607,7 +607,7 @@ public class VariantContextUtils {
}
// if we have more alternate alleles in the merged VC than in one or more of the
// original VCs, we need to strip out the GL/PLs (because they are no longer accurate)
// original VCs, we need to strip out the GL/PLs (because they are no longer accurate), as well as allele-dependent attributes like AC,AF
for ( VariantContext vc : VCs ) {
if (vc.alleles.size() == 1)
continue;
@ -615,6 +615,8 @@ public class VariantContextUtils {
logger.warn(String.format("Stripping PLs at %s due incompatible alleles merged=%s vs. single=%s",
genomeLocParser.createGenomeLoc(vc), alleles, vc.alleles));
genotypes = stripPLs(genotypes);
// this will remove stale AC,AF attributed from vc
calculateChromosomeCounts(vc, attributes, true);
break;
}
}

View File

@ -50,6 +50,7 @@ public abstract class BaseTest {
public static final String hg18Reference = "/seq/references/Homo_sapiens_assembly18/v0/Homo_sapiens_assembly18.fasta";
public static final String hg19Reference = "/seq/references/Homo_sapiens_assembly19/v1/Homo_sapiens_assembly19.fasta";
public static final String b36KGReference = "/humgen/1kg/reference/human_b36_both.fasta";
//public static final String b37KGReference = "/Users/depristo/Desktop/broadLocal/localData/human_g1k_v37.fasta";
public static final String b37KGReference = "/humgen/1kg/reference/human_g1k_v37.fasta";
public static final String GATKDataLocation = "/humgen/gsa-hpprojects/GATK/data/";
public static final String validationDataLocation = GATKDataLocation + "Validation_Data/";
@ -99,10 +100,10 @@ public abstract class BaseTest {
logger.setLevel(Level.WARN);
// find our file sources
if (!fileExist(hg18Reference) || !fileExist(hg19Reference) || !fileExist(b36KGReference)) {
logger.fatal("We can't locate the reference directories. Aborting!");
throw new RuntimeException("BaseTest setup failed: unable to locate the reference directories");
}
// if (!fileExist(hg18Reference) || !fileExist(hg19Reference) || !fileExist(b36KGReference)) {
// logger.fatal("We can't locate the reference directories. Aborting!");
// throw new RuntimeException("BaseTest setup failed: unable to locate the reference directories");
// }
}
/**

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.commandline;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.File;
public class ArgumentMatchSiteUnitTest {
@Test
public void testCommandLine() {
ArgumentMatchSite site = new ArgumentMatchSite(ArgumentMatchSource.COMMAND_LINE, 1);
Assert.assertEquals(site.getSource(), ArgumentMatchSource.COMMAND_LINE);
Assert.assertEquals(site.getIndex(), 1);
}
@Test
public void testFile() {
ArgumentMatchSource source = new ArgumentMatchSource(new File("test"));
ArgumentMatchSite site = new ArgumentMatchSite(source, 1);
Assert.assertEquals(site.getSource(), source);
Assert.assertEquals(site.getIndex(), 1);
}
@Test
public void testEquals() {
ArgumentMatchSource cmdLine = ArgumentMatchSource.COMMAND_LINE;
ArgumentMatchSite site1 = new ArgumentMatchSite(cmdLine, 1);
ArgumentMatchSite site2 = new ArgumentMatchSite(cmdLine, 2);
Assert.assertFalse(site1.equals(null));
Assert.assertTrue(site1.equals(site1));
Assert.assertFalse(site1.equals(site2));
Assert.assertFalse(site2.equals(site1));
Assert.assertTrue(site2.equals(site2));
}
@Test
public void testCompareTo() {
ArgumentMatchSource cmdLine = ArgumentMatchSource.COMMAND_LINE;
ArgumentMatchSite site1 = new ArgumentMatchSite(cmdLine, 1);
ArgumentMatchSite site2 = new ArgumentMatchSite(cmdLine, 2);
Assert.assertTrue(site1.compareTo(site1) == 0);
Assert.assertTrue(site1.compareTo(site2) < 0);
Assert.assertTrue(site2.compareTo(site1) > 0);
Assert.assertTrue(site2.compareTo(site2) == 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void testCompareToNull() {
new ArgumentMatchSite(ArgumentMatchSource.COMMAND_LINE, 0).compareTo(null);
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.commandline;
import org.broadinstitute.sting.BaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.File;
public class ArgumentMatchSourceUnitTest extends BaseTest {
@Test
public void testCommandLine() {
ArgumentMatchSource source = ArgumentMatchSource.COMMAND_LINE;
Assert.assertEquals(source.getType(), ArgumentMatchSourceType.CommandLine);
Assert.assertNull(source.getFile());
}
@Test
public void testFile() {
File f = new File("test");
ArgumentMatchSource source = new ArgumentMatchSource(f);
Assert.assertEquals(source.getType(), ArgumentMatchSourceType.File);
Assert.assertEquals(source.getFile(), f);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNullFile() {
new ArgumentMatchSource(null);
}
@Test
public void testEquals() {
ArgumentMatchSource cmdLine = ArgumentMatchSource.COMMAND_LINE;
ArgumentMatchSource fileA = new ArgumentMatchSource(new File("a"));
ArgumentMatchSource fileB = new ArgumentMatchSource(new File("b"));
Assert.assertFalse(cmdLine.equals(null));
Assert.assertTrue(cmdLine.equals(cmdLine));
Assert.assertFalse(cmdLine.equals(fileA));
Assert.assertFalse(cmdLine.equals(fileB));
Assert.assertFalse(fileA.equals(cmdLine));
Assert.assertTrue(fileA.equals(fileA));
Assert.assertFalse(fileA.equals(fileB));
Assert.assertFalse(fileB.equals(cmdLine));
Assert.assertFalse(fileB.equals(fileA));
Assert.assertTrue(fileB.equals(fileB));
}
@Test
public void testCompareTo() {
ArgumentMatchSource cmdLine = ArgumentMatchSource.COMMAND_LINE;
ArgumentMatchSource fileA = new ArgumentMatchSource(new File("a"));
ArgumentMatchSource fileB = new ArgumentMatchSource(new File("b"));
Assert.assertTrue(cmdLine.compareTo(cmdLine) == 0);
Assert.assertTrue(cmdLine.compareTo(fileA) < 0);
Assert.assertTrue(cmdLine.compareTo(fileB) < 0);
Assert.assertTrue(fileA.compareTo(cmdLine) > 0);
Assert.assertTrue(fileA.compareTo(fileA) == 0);
Assert.assertTrue(fileA.compareTo(fileB) < 0);
Assert.assertTrue(fileB.compareTo(cmdLine) > 0);
Assert.assertTrue(fileB.compareTo(fileA) > 0);
Assert.assertTrue(fileB.compareTo(fileB) == 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void testCompareToNull() {
ArgumentMatchSource.COMMAND_LINE.compareTo(null);
}
}

View File

@ -25,6 +25,7 @@
package org.broadinstitute.sting.commandline;
import org.apache.commons.io.FileUtils;
import org.broad.tribble.Feature;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.variantcontext.VariantContext;
@ -34,6 +35,8 @@ import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.EnumSet;
/**
@ -493,6 +496,7 @@ public class ParsingEngineUnitTest extends BaseTest {
Assert.assertNotNull(definition, "Invalid default argument name assigned");
}
@SuppressWarnings("unused")
private class CamelCaseArgProvider {
@Argument(doc="my arg")
Integer myArg;
@ -507,6 +511,7 @@ public class ParsingEngineUnitTest extends BaseTest {
parsingEngine.validate();
}
@SuppressWarnings("unused")
private class BooleanArgProvider {
@Argument(doc="my bool")
boolean myBool;
@ -561,6 +566,7 @@ public class ParsingEngineUnitTest extends BaseTest {
parsingEngine.validate();
}
@SuppressWarnings("unused")
private class MutuallyExclusiveArgProvider {
@Argument(doc="foo",exclusiveOf="bar")
Integer foo;
@ -618,6 +624,7 @@ public class ParsingEngineUnitTest extends BaseTest {
parsingEngine.addArgumentSource( MultipleArgumentCollectionProvider.class );
}
@SuppressWarnings("unused")
private class MultipleArgumentCollectionProvider {
@ArgumentCollection
RequiredArgProvider rap1 = new RequiredArgProvider();
@ -937,4 +944,23 @@ public class ParsingEngineUnitTest extends BaseTest {
VariantContextRodBindingArgProvider argProvider = new VariantContextRodBindingArgProvider();
parsingEngine.loadArgumentsIntoObject( argProvider );
}
@Test
public void argumentListTest() throws IOException {
File argsFile = BaseTest.createTempFile("args.", ".list");
try {
FileUtils.write(argsFile, "-I na12878.bam");
final String[] commandLine = new String[] {"-args", argsFile.getPath()};
parsingEngine.addArgumentSource(InputFileArgProvider.class);
parsingEngine.parse(commandLine);
parsingEngine.validate();
InputFileArgProvider argProvider = new InputFileArgProvider();
parsingEngine.loadArgumentsIntoObject(argProvider);
Assert.assertEquals(argProvider.inputFile, "na12878.bam", "Argument is not correctly initialized");
} finally {
FileUtils.deleteQuietly(argsFile);
}
}
}

View File

@ -18,7 +18,7 @@ public class BAQIntegrationTest extends WalkerTest {
// --------------------------------------------------------------------------------------------------------------
@Test
public void testPrintReadsNoBAQ() {
WalkerTestSpec spec = new WalkerTestSpec( baseCommand +" -baq OFF", 1, Arrays.asList("902197bf77ed5a828d50e08771685928"));
WalkerTestSpec spec = new WalkerTestSpec( baseCommand +" -baq OFF", 1, Arrays.asList("d97340a2bba2c6320d1ebeb86024a27c"));
executeTest(String.format("testPrintReadsNoBAQ"), spec);
}

View File

@ -96,8 +96,8 @@ public class CombineVariantsIntegrationTest extends WalkerTest {
@Test public void uniqueSNPs() { combine2("pilot2.snps.vcf4.genotypes.vcf", "yri.trio.gatk_glftrio.intersection.annotated.filtered.chr1.vcf", "", "78a49597f1abf1c738e67d50c8fbed2b"); }
@Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "9253d61ddb52c429adf0e153cef494ca"); }
@Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "5012dfe65cf7e7d8f014e97e4a996aea"); }
@Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "4c63bfa5f73793aaca42e130ec49f238"); }
@Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "86e326acbd8d2af8a6040eb146d92fc6"); }
@Test public void threeWayWithRefs() {
WalkerTestSpec spec = new WalkerTestSpec(

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.R;
import org.apache.commons.io.FileUtils;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.File;
public class RScriptLibraryUnitTest {
@Test
public void testProperties() {
Assert.assertEquals(RScriptLibrary.GSALIB.getLibraryName(), "gsalib");
Assert.assertEquals(RScriptLibrary.GSALIB.getResourcePath(), "gsalib.tar.gz");
}
@Test
public void testWriteTemp() {
File file = RScriptLibrary.GSALIB.writeTemp();
Assert.assertTrue(file.exists(), "R library was not written to temp file: " + file);
FileUtils.deleteQuietly(file);
}
}

View File

@ -0,0 +1,197 @@
package org.broadinstitute.sting.utils.io;
import org.apache.commons.io.FileUtils;
import org.broadinstitute.sting.BaseTest;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.testng.Assert;
import org.testng.annotations.Test;
public class IOUtilsUnitTest extends BaseTest {
@Test
public void testGoodTempDir() {
IOUtils.checkTempDir(new File("/tmp/queue"));
}
@Test(expectedExceptions=UserException.BadTmpDir.class)
public void testBadTempDir() {
IOUtils.checkTempDir(new File("/tmp"));
}
@Test
public void testAbsoluteSubDir() {
File subDir = IOUtils.absolute(new File("."), new File("/path/to/file"));
Assert.assertEquals(subDir, new File("/path/to/file"));
subDir = IOUtils.absolute(new File("/different/path"), new File("/path/to/file"));
Assert.assertEquals(subDir, new File("/path/to/file"));
subDir = IOUtils.absolute(new File("/different/path"), new File("."));
Assert.assertEquals(subDir, new File("/different/path"));
}
@Test
public void testRelativeSubDir() throws IOException {
File subDir = IOUtils.absolute(new File("."), new File("path/to/file"));
Assert.assertEquals(subDir.getCanonicalFile(), new File("path/to/file").getCanonicalFile());
subDir = IOUtils.absolute(new File("/different/path"), new File("path/to/file"));
Assert.assertEquals(subDir, new File("/different/path/path/to/file"));
}
@Test
public void testDottedSubDir() throws IOException {
File subDir = IOUtils.absolute(new File("."), new File("path/../to/file"));
Assert.assertEquals(subDir.getCanonicalFile(), new File("path/../to/./file").getCanonicalFile());
subDir = IOUtils.absolute(new File("."), new File("/path/../to/file"));
Assert.assertEquals(subDir, new File("/path/../to/file"));
subDir = IOUtils.absolute(new File("/different/../path"), new File("path/to/file"));
Assert.assertEquals(subDir, new File("/different/../path/path/to/file"));
subDir = IOUtils.absolute(new File("/different/./path"), new File("/path/../to/file"));
Assert.assertEquals(subDir, new File("/path/../to/file"));
}
@Test
public void testTempDir() {
File tempDir = IOUtils.tempDir("Q-Unit-Test", "", new File("queueTempDirToDelete"));
Assert.assertTrue(tempDir.exists());
Assert.assertFalse(tempDir.isFile());
Assert.assertTrue(tempDir.isDirectory());
boolean deleted = IOUtils.tryDelete(tempDir);
Assert.assertTrue(deleted);
Assert.assertFalse(tempDir.exists());
}
@Test
public void testDirLevel() {
File dir = IOUtils.dirLevel(new File("/path/to/directory"), 1);
Assert.assertEquals(dir, new File("/path"));
dir = IOUtils.dirLevel(new File("/path/to/directory"), 2);
Assert.assertEquals(dir, new File("/path/to"));
dir = IOUtils.dirLevel(new File("/path/to/directory"), 3);
Assert.assertEquals(dir, new File("/path/to/directory"));
dir = IOUtils.dirLevel(new File("/path/to/directory"), 4);
Assert.assertEquals(dir, new File("/path/to/directory"));
}
@Test
public void testAbsolute() {
File dir = IOUtils.absolute(new File("/path/./to/./directory/."));
Assert.assertEquals(dir, new File("/path/to/directory"));
dir = IOUtils.absolute(new File("/"));
Assert.assertEquals(dir, new File("/"));
dir = IOUtils.absolute(new File("/."));
Assert.assertEquals(dir, new File("/"));
dir = IOUtils.absolute(new File("/././."));
Assert.assertEquals(dir, new File("/"));
dir = IOUtils.absolute(new File("/./directory/."));
Assert.assertEquals(dir, new File("/directory"));
dir = IOUtils.absolute(new File("/./directory/./"));
Assert.assertEquals(dir, new File("/directory"));
dir = IOUtils.absolute(new File("/./directory./"));
Assert.assertEquals(dir, new File("/directory."));
dir = IOUtils.absolute(new File("/./.directory/"));
Assert.assertEquals(dir, new File("/.directory"));
}
@Test
public void testTail() throws IOException {
List<String> lines = Arrays.asList(
"chr18_random 4262 3154410390 50 51",
"chr19_random 301858 3154414752 50 51",
"chr21_random 1679693 3154722662 50 51",
"chr22_random 257318 3156435963 50 51",
"chrX_random 1719168 3156698441 50 51");
List<String> tail = IOUtils.tail(new File(BaseTest.hg18Reference + ".fai"), 5);
Assert.assertEquals(tail.size(), 5);
for (int i = 0; i < 5; i++)
Assert.assertEquals(tail.get(i), lines.get(i));
}
@Test
public void testWriteSystemFile() throws IOException {
File temp = createTempFile("temp.", ".properties");
try {
IOUtils.writeResource(new Resource("StingText.properties", null), temp);
} finally {
FileUtils.deleteQuietly(temp);
}
}
@Test
public void testWriteSystemTempFile() throws IOException {
File temp = IOUtils.writeTempResource(new Resource("StingText.properties", null));
try {
Assert.assertTrue(temp.getName().startsWith("StingText"), "File does not start with 'StingText.': " + temp);
Assert.assertTrue(temp.getName().endsWith(".properties"), "File does not end with '.properties': " + temp);
} finally {
FileUtils.deleteQuietly(temp);
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMissingSystemFile() throws IOException {
File temp = createTempFile("temp.", ".properties");
try {
IOUtils.writeResource(new Resource("MissingStingText.properties", null), temp);
} finally {
FileUtils.deleteQuietly(temp);
}
}
@Test
public void testWriteRelativeFile() throws IOException {
File temp = createTempFile("temp.", ".properties");
try {
IOUtils.writeResource(new Resource("/StingText.properties", IOUtils.class), temp);
} finally {
FileUtils.deleteQuietly(temp);
}
}
@Test
public void testWriteRelativeTempFile() throws IOException {
File temp = IOUtils.writeTempResource(new Resource("/StingText.properties", IOUtils.class));
try {
Assert.assertTrue(temp.getName().startsWith("StingText"), "File does not start with 'StingText.': " + temp);
Assert.assertTrue(temp.getName().endsWith(".properties"), "File does not end with '.properties': " + temp);
} finally {
FileUtils.deleteQuietly(temp);
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMissingRelativeFile() throws IOException {
File temp = createTempFile("temp.", ".properties");
try {
// Looking for /org/broadinstitute/sting/utils/file/StingText.properties
IOUtils.writeResource(new Resource("StingText.properties", IOUtils.class), temp);
} finally {
FileUtils.deleteQuietly(temp);
}
}
@Test
public void testResourceProperties() {
Resource resource = new Resource("foo", Resource.class);
Assert.assertEquals(resource.getPath(), "foo");
Assert.assertEquals(resource.getRelativeClass(), Resource.class);
}
}

View File

@ -0,0 +1,517 @@
/*
* Copyright (c) 2011, The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.utils.runtime;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.broadinstitute.sting.BaseTest;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.io.IOUtils;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ProcessControllerUnitTest extends BaseTest {
private static final String NL = String.format("%n");
@Test(timeOut = 60 * 1000)
public void testDestroyThreadLocal() throws InterruptedException {
for (int i = 0; i < 3; i++) {
final ProcessController controller = ProcessController.getThreadLocal();
final ProcessSettings job = new ProcessSettings(
new String[] {"sh", "-c", "echo Hello World && sleep 600 && echo Goodbye"});
job.getStdoutSettings().setBufferSize(-1);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("BACK: Starting on background thread");
ProcessOutput result = controller.exec(job);
// Assert in background thread doesn't make it to main thread but does print a trace.
Assert.assertTrue(result.getExitValue() != 0, "Destroy-attempted job returned zero exit status");
System.out.println("BACK: Background thread exiting");
}
});
System.out.println("MAIN: Starting background thread");
t.start();
System.out.println("MAIN: Sleeping main thread 3s");
Thread.sleep(3000);
System.out.println("MAIN: Destroying job");
controller.tryDestroy();
System.out.println("MAIN: Not waiting on background thread to exit");
// Using standard java.io this was blocking on linux.
// TODO: try again with NIO.
//t.join();
//System.out.println("MAIN: Background thread exited");
}
}
@Test
public void testReuseAfterError() {
ProcessController controller = new ProcessController();
ProcessSettings job;
for (int i = 0; i < 3; i++) {
// Test bad command
job = new ProcessSettings(new String[] {"no_such_command"});
try {
controller.exec(job);
} catch (ReviewedStingException e) {
/* Was supposed to throw an exception */
}
// Test exit != 0
job = new ProcessSettings(new String[] {"cat", "non_existent_file"});
int exitValue = controller.exec(job).getExitValue();
Assert.assertTrue(exitValue != 0, "'cat' non existent file returned 0");
// Text success
job = new ProcessSettings(new String[] {"echo", "Hello World"});
exitValue = controller.exec(job).getExitValue();
Assert.assertEquals(exitValue, 0, "Echo failed");
}
}
@Test
public void testEnvironment() {
String key = "MY_NEW_VAR";
String value = "value is here";
ProcessSettings job = new ProcessSettings(new String[] {"sh", "-c", "echo $"+key});
job.getStdoutSettings().setBufferSize(-1);
job.setRedirectErrorStream(true);
Map<String, String> env = new HashMap<String, String>(System.getenv());
env.put(key, value);
job.setEnvironment(env);
ProcessController controller = new ProcessController();
ProcessOutput result = controller.exec(job);
int exitValue = result.getExitValue();
Assert.assertEquals(exitValue, 0, "Echo environment variable failed");
Assert.assertEquals(result.getStdout().getBufferString(), value + NL, "Echo environment returned unexpected output");
}
@Test
public void testDirectory() throws IOException {
File dir = null;
try {
dir = IOUtils.tempDir("temp.", "").getCanonicalFile();
ProcessSettings job = new ProcessSettings(new String[] {"pwd"});
job.getStdoutSettings().setBufferSize(-1);
job.setRedirectErrorStream(true);
job.setDirectory(dir);
ProcessController controller = new ProcessController();
ProcessOutput result = controller.exec(job);
int exitValue = result.getExitValue();
Assert.assertEquals(exitValue, 0, "Getting working directory failed");
Assert.assertEquals(result.getStdout().getBufferString(), dir.getAbsolutePath() + NL,
"Setting/getting working directory returned unexpected output");
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testReadStdInBuffer() {
String bufferText = "Hello from buffer";
ProcessSettings job = new ProcessSettings(new String[] {"cat"});
job.getStdoutSettings().setBufferSize(-1);
job.setRedirectErrorStream(true);
job.getStdinSettings().setInputBuffer(bufferText);
ProcessController controller = new ProcessController();
ProcessOutput output = controller.exec(job);
Assert.assertEquals(output.getStdout().getBufferString(), bufferText,
"Unexpected output from cat stdin buffer");
}
@Test
public void testReadStdInFile() {
File input = null;
try {
String fileText = "Hello from file";
input = IOUtils.writeTempFile(fileText, "stdin.", ".txt", null);
ProcessSettings job = new ProcessSettings(new String[] {"cat"});
job.getStdoutSettings().setBufferSize(-1);
job.setRedirectErrorStream(true);
job.getStdinSettings().setInputFile(input);
ProcessController controller = new ProcessController();
ProcessOutput output = controller.exec(job);
Assert.assertEquals(output.getStdout().getBufferString(), fileText,
"Unexpected output from cat stdin file");
} finally {
FileUtils.deleteQuietly(input);
}
}
@Test
public void testWriteStdOut() {
ProcessSettings job = new ProcessSettings(new String[] {"echo", "Testing to stdout"});
// Not going to call the System.setOut() for now. Just running a basic visual test.
job.getStdoutSettings().printStandard(true);
job.setRedirectErrorStream(true);
System.out.println("testWriteStdOut: Writing two lines to std out...");
ProcessController controller = new ProcessController();
controller.exec(job);
job.setCommand(new String[]{"cat", "non_existent_file"});
controller.exec(job);
System.out.println("testWriteStdOut: ...two lines should have been printed to std out");
}
@Test
public void testErrorToOut() throws IOException {
File outFile = null;
File errFile = null;
try {
outFile = BaseTest.createTempFile("temp", "");
errFile = BaseTest.createTempFile("temp", "");
ProcessSettings job = new ProcessSettings(new String[]{"cat", "non_existent_file"});
job.getStdoutSettings().setOutputFile(outFile);
job.getStdoutSettings().setBufferSize(-1);
job.getStderrSettings().setOutputFile(errFile);
job.getStderrSettings().setBufferSize(-1);
job.setRedirectErrorStream(true);
ProcessOutput result = new ProcessController().exec(job);
int exitValue = result.getExitValue();
Assert.assertTrue(exitValue != 0, "'cat' non existent file returned 0");
String fileString, bufferString;
fileString = FileUtils.readFileToString(outFile);
Assert.assertTrue(fileString.length() > 0, "Out file was length 0");
bufferString = result.getStdout().getBufferString();
Assert.assertTrue(bufferString.length() > 0, "Out buffer was length 0");
Assert.assertFalse(result.getStdout().isBufferTruncated(), "Out buffer was truncated");
Assert.assertEquals(bufferString.length(), fileString.length(), "Out buffer length did not match file length");
fileString = FileUtils.readFileToString(errFile);
Assert.assertEquals(fileString, "", "Unexpected output to err file");
bufferString = result.getStderr().getBufferString();
Assert.assertEquals(bufferString, "", "Unexepected output to err buffer");
} finally {
FileUtils.deleteQuietly(outFile);
FileUtils.deleteQuietly(errFile);
}
}
@Test
public void testErrorToErr() throws IOException {
File outFile = null;
File errFile = null;
try {
outFile = BaseTest.createTempFile("temp", "");
errFile = BaseTest.createTempFile("temp", "");
ProcessSettings job = new ProcessSettings(new String[]{"cat", "non_existent_file"});
job.getStdoutSettings().setOutputFile(outFile);
job.getStdoutSettings().setBufferSize(-1);
job.getStderrSettings().setOutputFile(errFile);
job.getStderrSettings().setBufferSize(-1);
job.setRedirectErrorStream(false);
ProcessOutput result = new ProcessController().exec(job);
int exitValue = result.getExitValue();
Assert.assertTrue(exitValue != 0, "'cat' non existent file returned 0");
String fileString, bufferString;
fileString = FileUtils.readFileToString(errFile);
Assert.assertTrue(fileString.length() > 0, "Err file was length 0");
bufferString = result.getStderr().getBufferString();
Assert.assertTrue(bufferString.length() > 0, "Err buffer was length 0");
Assert.assertFalse(result.getStderr().isBufferTruncated(), "Err buffer was truncated");
Assert.assertEquals(bufferString.length(), fileString.length(), "Err buffer length did not match file length");
fileString = FileUtils.readFileToString(outFile);
Assert.assertEquals(fileString, "", "Unexpected output to out file");
bufferString = result.getStdout().getBufferString();
Assert.assertEquals(bufferString, "", "Unexepected output to out buffer");
} finally {
FileUtils.deleteQuietly(outFile);
FileUtils.deleteQuietly(errFile);
}
}
private static final String TRUNCATE_TEXT = "Hello World";
private static final byte[] TRUNCATE_OUTPUT_BYTES = (TRUNCATE_TEXT + NL).getBytes();
/**
* @return Test truncating content vs. not truncating (run at -1/+1 size)
*/
@DataProvider(name = "truncateSizes")
public Object[][] getTruncateBufferSizes() {
int l = TRUNCATE_OUTPUT_BYTES.length;
return new Object[][]{
new Object[]{0, 0},
new Object[]{l, l},
new Object[]{l + 1, l},
new Object[]{l - 1, l - 1}
};
}
@Test(dataProvider = "truncateSizes")
public void testTruncateBuffer(int truncateLen, int expectedLen) {
byte[] expected = Arrays.copyOf(TRUNCATE_OUTPUT_BYTES, expectedLen);
String[] command = {"echo", TRUNCATE_TEXT};
ProcessController controller = new ProcessController();
ProcessSettings job = new ProcessSettings(command);
job.getStdoutSettings().setBufferSize(truncateLen);
ProcessOutput result = controller.exec(job);
int exitValue = result.getExitValue();
Assert.assertEquals(exitValue, 0,
String.format("Echo returned %d: %s", exitValue, TRUNCATE_TEXT));
byte[] bufferBytes = result.getStdout().getBufferBytes();
Assert.assertEquals(bufferBytes, expected,
String.format("Output buffer didn't match (%d vs %d)", expected.length, bufferBytes.length));
boolean truncated = result.getStdout().isBufferTruncated();
Assert.assertEquals(truncated, TRUNCATE_OUTPUT_BYTES.length > truncateLen,
"Unexpected buffer truncation result");
}
private static final String[] LONG_COMMAND = getLongCommand();
private static final String LONG_COMMAND_STRING = StringUtils.join(LONG_COMMAND, " ");
private static final String LONG_COMMAND_DESCRIPTION = "<long command>";
@DataProvider(name = "echoCommands")
public Object[][] getEchoCommands() {
new EchoCommand(new String[]{"echo", "Hello", "World"}, "Hello World" + NL);
new EchoCommand(new String[]{"echo", "'Hello", "World"}, "'Hello World" + NL);
new EchoCommand(new String[]{"echo", "Hello", "World'"}, "Hello World'" + NL);
new EchoCommand(new String[]{"echo", "'Hello", "World'"}, "'Hello World'" + NL);
String[] longCommand = new String[LONG_COMMAND.length + 1];
longCommand[0] = "echo";
System.arraycopy(LONG_COMMAND, 0, longCommand, 1, LONG_COMMAND.length);
new EchoCommand(longCommand, LONG_COMMAND_STRING + NL) {
@Override
public String toString() {
return LONG_COMMAND_DESCRIPTION;
}
};
return TestDataProvider.getTests(EchoCommand.class);
}
@Test(dataProvider = "echoCommands")
public void testEcho(EchoCommand script) throws IOException {
File outputFile = null;
try {
outputFile = BaseTest.createTempFile("temp", "");
ProcessSettings job = new ProcessSettings(script.command);
if (script.output != null) {
job.getStdoutSettings().setOutputFile(outputFile);
job.getStdoutSettings().setBufferSize(script.output.getBytes().length);
}
ProcessOutput result = new ProcessController().exec(job);
int exitValue = result.getExitValue();
Assert.assertEquals(exitValue, 0,
String.format("Echo returned %d: %s", exitValue, script));
if (script.output != null) {
String fileString = FileUtils.readFileToString(outputFile);
Assert.assertEquals(fileString, script.output,
String.format("Output file didn't match (%d vs %d): %s",
fileString.length(), script.output.length(), script));
String bufferString = result.getStdout().getBufferString();
Assert.assertEquals(bufferString, script.output,
String.format("Output content didn't match (%d vs %d): %s",
bufferString.length(), script.output.length(), script));
Assert.assertFalse(result.getStdout().isBufferTruncated(),
"Output content was truncated: " + script);
}
} finally {
FileUtils.deleteQuietly(outputFile);
}
}
@Test(expectedExceptions = ReviewedStingException.class)
public void testUnableToStart() {
ProcessSettings job = new ProcessSettings(new String[]{"no_such_command"});
new ProcessController().exec(job);
}
@DataProvider(name = "scriptCommands")
public Object[][] getScriptCommands() {
new ScriptCommand(true, "echo Hello World", "Hello World" + NL);
new ScriptCommand(false, "echo 'Hello World", null);
new ScriptCommand(false, "echo Hello World'", null);
new ScriptCommand(true, "echo 'Hello World'", "Hello World" + NL);
new ScriptCommand(true, "echo \"Hello World\"", "Hello World" + NL);
new ScriptCommand(false, "no_such_echo Hello World", null);
new ScriptCommand(true, "echo #", NL);
new ScriptCommand(true, "echo \\#", "#" + NL);
new ScriptCommand(true, "echo \\\\#", "\\#" + NL);
new ScriptCommand(true, "echo " + LONG_COMMAND_STRING, LONG_COMMAND_STRING + NL) {
@Override
public String toString() {
return LONG_COMMAND_DESCRIPTION;
}
};
return TestDataProvider.getTests(ScriptCommand.class);
}
@Test(dataProvider = "scriptCommands")
public void testScript(ScriptCommand script) throws IOException {
File scriptFile = null;
File outputFile = null;
try {
scriptFile = writeScript(script.content);
outputFile = BaseTest.createTempFile("temp", "");
ProcessSettings job = new ProcessSettings(new String[]{"sh", scriptFile.getAbsolutePath()});
if (script.output != null) {
job.getStdoutSettings().setOutputFile(outputFile);
job.getStdoutSettings().setBufferSize(script.output.getBytes().length);
}
ProcessOutput result = new ProcessController().exec(job);
int exitValue = result.getExitValue();
Assert.assertEquals(exitValue == 0, script.succeed,
String.format("Script returned %d: %s", exitValue, script));
if (script.output != null) {
String fileString = FileUtils.readFileToString(outputFile);
Assert.assertEquals(fileString, script.output,
String.format("Output file didn't match (%d vs %d): %s",
fileString.length(), script.output.length(), script));
String bufferString = result.getStdout().getBufferString();
Assert.assertEquals(bufferString, script.output,
String.format("Output content didn't match (%d vs %d): %s",
bufferString.length(), script.output.length(), script));
Assert.assertFalse(result.getStdout().isBufferTruncated(),
"Output content was truncated: " + script);
}
} finally {
FileUtils.deleteQuietly(scriptFile);
FileUtils.deleteQuietly(outputFile);
}
}
private static String[] getLongCommand() {
// This command fails on some systems with a 4096 character limit when run via the old sh -c "echo ...",
// but works on the same systems when run via sh <script>
int cnt = 500;
String[] command = new String[cnt];
for (int i = 1; i <= cnt; i++) {
command[i - 1] = String.format("%03d______", i);
}
return command;
}
private static File writeScript(String contents) {
try {
File file = BaseTest.createTempFile("temp", "");
FileUtils.writeStringToFile(file, contents);
return file;
} catch (IOException e) {
throw new UserException.BadTmpDir(e.getMessage());
}
}
private static class EchoCommand extends TestDataProvider {
public final String[] command;
public final String output;
public EchoCommand(String[] command, String output) {
super(EchoCommand.class);
this.command = command;
this.output = output;
}
@Override
public String toString() {
return StringUtils.join(command, " ");
}
}
public static class ScriptCommand extends TestDataProvider {
public final boolean succeed;
public final String content;
public final String output;
public ScriptCommand(boolean succeed, String content, String output) {
super(ScriptCommand.class);
this.succeed = succeed;
this.content = content;
this.output = output;
}
@Override
public String toString() {
return content;
}
}
}

View File

@ -28,8 +28,8 @@ import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.testng.annotations.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
@ -39,10 +39,16 @@ import java.net.URLClassLoader;
public class VCFJarClassLoadingUnitTest {
@Test
public void testVCFJarClassLoading() throws ClassNotFoundException, MalformedURLException {
URI vcfURI = new File("dist/vcf.jar").toURI();
URI tribbleURI = getTribbleJarFile().toURI();
URL[] jarURLs;
ClassLoader classLoader = new URLClassLoader(new URL[] {vcfURI.toURL(),tribbleURI.toURL()}, null);
try {
jarURLs = new URL[] { getVCFJarFile().toURI().toURL(), getTribbleJarFile().toURI().toURL() };
}
catch ( FileNotFoundException e ) {
throw new ReviewedStingException("Could not find the VCF jar and/or its dependencies", e);
}
ClassLoader classLoader = new URLClassLoader(jarURLs, null);
classLoader.loadClass("org.broadinstitute.sting.utils.variantcontext.VariantContext");
classLoader.loadClass("org.broadinstitute.sting.utils.codecs.vcf.VCFCodec");
classLoader.loadClass("org.broadinstitute.sting.utils.codecs.vcf.VCF3Codec");
@ -51,19 +57,49 @@ public class VCFJarClassLoadingUnitTest {
}
/**
* A very unsafe way of determining the current location of the Tribble jar file. Assumes that
* the tribble jar (as opposed to the constituent tribble classes) is on the classpath.
* Locates the tribble jar within the dist directory.
*
* This method might or might not work when built via IntelliJ's debugger.
* Makes the horrible assumption that tests will always be run from the root of a Sting clone,
* but this is much less problematic than using the classpath to locate tribble, since
* the classpath won't explicitly contain tribble when we're testing the fully-packaged
* GATK jar.
*
* @return The file representing the tribble jar.
* @return The tribble jar file, if found
* @throws FileNotFoundException If we couldn't locate a tribble jar within the dist directory
*/
private File getTribbleJarFile() {
String[] classPath = System.getProperty("java.class.path").split(File.pathSeparator);
for(String classPathEntry: classPath) {
if(classPathEntry.contains("tribble"))
return new File(classPathEntry);
private File getTribbleJarFile() throws FileNotFoundException {
File distDir = new File("dist");
if ( ! distDir.isDirectory() ) {
throw new FileNotFoundException("The dist directory does not exist");
}
throw new ReviewedStingException("Unable to find Tribble jar file");
for ( File distDirEntry : distDir.listFiles() ) {
if ( distDirEntry.getName().startsWith("tribble") && distDirEntry.getName().endsWith(".jar") ) {
return distDirEntry;
}
}
throw new FileNotFoundException("Could not find a tribble jar file in the dist directory.");
}
/**
* Locates the vcf jar within the dist directory.
*
* Makes the horrible assumption that tests will always be run from the root of a Sting clone,
* but this is much less problematic than using the classpath to locate vcf.jar, since
* the classpath won't explicitly contain vcf.jar when we're testing the fully-packaged
* GATK jar.
*
* @return The vcf jar file, if found
* @throws FileNotFoundException If we couldn't locate a vcf jar within the dist directory
*/
private File getVCFJarFile() throws FileNotFoundException {
File vcfJar = new File("dist/vcf.jar");
if ( ! vcfJar.exists() ) {
throw new FileNotFoundException("Could not find dist/vcf.jar");
}
return vcfJar;
}
}

View File

@ -179,13 +179,19 @@
</xsl:when>
</xsl:choose>
</xsl:for-each>
<xsl:for-each select="dir">
<fileset dir="{$staging.dir}">
<xsl:attribute name="includes">
<xsl:value-of select="concat(@name,'/**')"/>
</xsl:attribute>
</fileset>
</xsl:for-each>
<xsl:for-each select="dir">
<xsl:variable name="includes">
<xsl:choose>
<xsl:when test="@includes = ''">
<xsl:value-of select="concat(@name,'/**')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@name,'/',@includes)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fileset dir="{$staging.dir}" includes="{$includes}"/>
</xsl:for-each>
</xsl:template>
<!-- Determine the short name (filename w/o directory structure of the given filename -->

View File

@ -32,6 +32,8 @@
<package name="org.broad.tribble.**" />
<!-- Workaround - depend on the logger impl required by JEXL -->
<package name="org.apache.commons.logging.impl" />
<!-- R packages -->
<dir name="org/broadinstitute/sting/utils/R" includes="*.tar.gz" />
</dependencies>
</executable>
<resources>

View File

@ -65,6 +65,10 @@
<package name="ca.mcgill.mcb.pcingola.**" />
<file path="snpEff_genes.ftl" />
<file path="snpEff_summary.ftl" />
<!-- R scripts -->
<dir name="org/broadinstitute/sting/queue" includes="**/*.R" />
</dependencies>
<modules>
<module file="GATKEngine.xml"/>

View File

@ -205,7 +205,6 @@ class MethodsDevelopmentCallingPipeline extends QScript {
// 1.) Unified Genotyper Base
class GenotyperBase (t: Target) extends UnifiedGenotyper with UNIVERSAL_GATK_ARGS {
this.memoryLimit = 3
this.reference_sequence = t.reference
this.intervalsString ++= List(t.intervals)
this.scatterCount = 140
@ -232,6 +231,7 @@ class MethodsDevelopmentCallingPipeline extends QScript {
// 1b.) Call Indels with UG
class indelCall (t: Target) extends GenotyperBase(t) {
this.memoryLimit = 6
this.out = t.rawIndelVCF
this.glm = org.broadinstitute.sting.gatk.walkers.genotyper.GenotypeLikelihoodsCalculationModel.Model.INDEL
this.baq = org.broadinstitute.sting.utils.baq.BAQ.CalculationMode.OFF
@ -259,7 +259,6 @@ class MethodsDevelopmentCallingPipeline extends QScript {
// 3.) Variant Quality Score Recalibration - Generate Recalibration table
class VQSR(t: Target, goldStandard: Boolean) extends VariantRecalibrator with UNIVERSAL_GATK_ARGS {
this.memoryLimit = 4
this.nt = 2
this.reference_sequence = t.reference
this.intervalsString ++= List(t.intervals)

View File

@ -33,6 +33,7 @@ import org.broadinstitute.sting.queue.engine.{QGraphSettings, QGraph}
import collection.JavaConversions._
import org.broadinstitute.sting.utils.classloader.PluginManager
import org.broadinstitute.sting.utils.exceptions.UserException
import org.broadinstitute.sting.utils.io.IOUtils
/**
* Entry point of Queue. Compiles and runs QScripts passed in to the command line.
@ -48,7 +49,6 @@ object QCommandLine extends Logging {
val shutdownHook = new Thread {
override def run() {
logger.info("Shutting down jobs. Please wait...")
ProcessController.shutdown()
qCommandLine.shutdown()
}
}
@ -56,8 +56,12 @@ object QCommandLine extends Logging {
Runtime.getRuntime.addShutdownHook(shutdownHook)
try {
CommandLineProgram.start(qCommandLine, argv);
Runtime.getRuntime.removeShutdownHook(shutdownHook)
CommandLineProgram.start(qCommandLine, argv)
try {
Runtime.getRuntime.removeShutdownHook(shutdownHook)
} catch {
case _ => /* ignore, example 'java.lang.IllegalStateException: Shutdown in progress' */
}
if (CommandLineProgram.result != 0)
System.exit(CommandLineProgram.result);
} catch {
@ -80,6 +84,7 @@ class QCommandLine extends CommandLineProgram with Logging {
private val qScriptManager = new QScriptManager
private val qGraph = new QGraph
private var qScriptClasses: File = _
private var shuttingDown = false
private lazy val pluginManager = {
qScriptClasses = IOUtils.tempDir("Q-Classes", "", settings.qSettings.tempDirectory)
@ -118,10 +123,16 @@ class QCommandLine extends CommandLineProgram with Logging {
script.onExecutionDone(qGraph.getFunctionsAndStatus(script.functions), qGraph.success)
if ( ! settings.disableJobReport ) {
val jobStringName = (QScriptUtils.?(settings.jobReportFile)).getOrElse(settings.qSettings.jobNamePrefix + ".jobreport.txt")
val jobReportFile = new File(jobStringName)
logger.info("Writing JobLogging GATKReport to file " + jobReportFile)
QJobReport.printReport(qGraph.getFunctionsAndStatus(script.functions), jobReportFile)
QJobReport.plotReport(settings.rScriptArgs, jobReportFile)
if (!shuttingDown) {
val reportFile = new File(jobStringName)
logger.info("Writing JobLogging GATKReport to file " + reportFile)
QJobReport.printReport(qGraph.getFunctionsAndStatus(script.functions), reportFile)
val pdfFile = new File(jobStringName + ".pdf")
logger.info("Plotting JobLogging GATKReport to file " + pdfFile)
QJobReport.plotReport(settings.rScriptArgs, reportFile, pdfFile)
}
}
}
@ -163,6 +174,7 @@ class QCommandLine extends CommandLineProgram with Logging {
Arrays.asList(new ScalaCompoundArgumentTypeDescriptor)
def shutdown() = {
shuttingDown = true
qGraph.shutdown()
if (qScriptClasses != null) IOUtils.tryDelete(qScriptClasses)
}

View File

@ -2,7 +2,7 @@ package org.broadinstitute.sting.queue
import scala.tools.nsc.{Global, Settings}
import scala.tools.nsc.io.PlainFile
import org.broadinstitute.sting.queue.util.{Logging, IOUtils}
import org.broadinstitute.sting.queue.util.Logging
import collection.JavaConversions._
import java.io.File
import scala.tools.nsc.reporters.AbstractReporter
@ -20,7 +20,7 @@ class QScriptManager() extends Logging {
* Compiles and loads the scripts in the files into the current classloader.
* Heavily based on scala/src/compiler/scala/tools/ant/Scalac.scala
*/
def loadScripts(scripts: List[File], tempDir: File) = {
def loadScripts(scripts: List[File], tempDir: File) {
if (scripts.size > 0) {
val settings = new Settings((error: String) => logger.error(error))
settings.deprecation.value = true
@ -63,7 +63,7 @@ object QScriptManager extends Logging {
* Heavily based on scala/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
*/
private class Log4JReporter(val settings: Settings) extends AbstractReporter {
def displayPrompt = throw new UnsupportedOperationException("Unable to prompt the user. Prompting should be off.")
def displayPrompt { throw new UnsupportedOperationException("Unable to prompt the user. Prompting should be off.") }
/**
* Displays the message at position with severity.
@ -71,7 +71,7 @@ object QScriptManager extends Logging {
* @param msg Message to display.
* @param severity Severity of the event.
*/
def display(posIn: Position, msg: String, severity: Severity) = {
def display(posIn: Position, msg: String, severity: Severity) {
severity.count += 1
val level = severity match {
case INFO => Level.INFO
@ -87,7 +87,6 @@ object QScriptManager extends Logging {
case NoPosition =>
printMessage(level, msg)
case _ =>
val buf = new StringBuilder(msg)
val file = pos.source.file
printMessage(level, file.name+":"+pos.line+": "+msg)
printSourceLine(level, pos)
@ -97,7 +96,7 @@ object QScriptManager extends Logging {
/**
* Prints a summary count of warnings and errors.
*/
def printSummary() = {
def printSummary() {
if (WARNING.count > 0)
printMessage(Level.WARN, countElementsAsString(WARNING.count, "warning") + " found")
if (ERROR.count > 0)
@ -119,15 +118,16 @@ object QScriptManager extends Logging {
* @param level Severity level.
* @param pos Position in the file of the event.
*/
private def printColumnMarker(level: Level, pos: Position) =
private def printColumnMarker(level: Level, pos: Position) {
if (pos.isDefined) { printMessage(level, " " * (pos.column - 1) + "^") }
}
/**
* Prints the message at the severity level.
* @param level Severity level.
* @param message Message content.
*/
private def printMessage(level: Level, message: String) = {
private def printMessage(level: Level, message: String) {
logger.log(level, message)
}
}

View File

@ -26,7 +26,8 @@ package org.broadinstitute.sting.queue.engine
import org.broadinstitute.sting.queue.function.CommandLineFunction
import java.io.File
import org.broadinstitute.sting.queue.util.{Logging, IOUtils}
import org.broadinstitute.sting.queue.util.Logging
import org.broadinstitute.sting.utils.io.IOUtils
/**
* Runs a command line function.
@ -69,7 +70,7 @@ trait CommandLineJobRunner extends JobRunner[CommandLineFunction] with Logging {
override def init() {
super.init()
var exec = new StringBuilder
val exec = new StringBuilder
var dirs = Set.empty[File]
for (dir <- function.jobDirectories)

View File

@ -2,7 +2,10 @@ package org.broadinstitute.sting.queue.engine
import org.broadinstitute.sting.queue.function.QFunction
import java.io.{StringWriter, PrintWriter}
import org.broadinstitute.sting.queue.util.{Logging, IOUtils}
import org.broadinstitute.sting.queue.util.Logging
import org.broadinstitute.sting.utils.io.IOUtils
import org.apache.commons.io.FileUtils
import org.apache.commons.lang.StringUtils
/**
* An edge in the QGraph that runs a QFunction.
@ -150,26 +153,19 @@ class FunctionEdge(val function: QFunction, val inputs: QNode, val outputs: QNod
/**
* Outputs the last lines of the error logs.
*/
private def tailError() = {
private def tailError() {
val errorFile = functionErrorFile
if (IOUtils.waitFor(errorFile, 120)) {
val maxLines = 100
val tailLines = IOUtils.tail(errorFile, maxLines)
val nl = "%n".format()
val summary = if (tailLines.size > maxLines) "Last %d lines".format(maxLines) else "Contents"
logger.error("%s of %s:%n%s".format(summary, errorFile, tailLines.mkString(nl)))
logger.error("%s of %s:%n%s".format(summary, errorFile, StringUtils.join(tailLines, nl)))
} else {
logger.error("Unable to access log file: %s".format(errorFile))
}
}
/**
* Writes the contents of the error to the error file.
*/
private def writeError(content: String) {
IOUtils.writeContents(functionErrorFile, content)
}
/**
* Writes the stack trace to the error file.
*/
@ -178,8 +174,8 @@ class FunctionEdge(val function: QFunction, val inputs: QNode, val outputs: QNod
val printWriter = new PrintWriter(stackTrace)
printWriter.println(function.description)
e.printStackTrace(printWriter)
printWriter.close
IOUtils.writeContents(functionErrorFile, stackTrace.toString)
printWriter.close()
FileUtils.writeStringToFile(functionErrorFile, stackTrace.toString)
}
def getRunInfo = {

View File

@ -2,8 +2,8 @@ package org.broadinstitute.sting.queue.engine
import org.broadinstitute.sting.queue.function.InProcessFunction
import java.util.Date
import org.broadinstitute.sting.queue.util.{Logging, IOUtils}
import org.broadinstitute.sting.utils.Utils
import org.apache.commons.io.FileUtils
/**
* Runs a function that executes in process and does not fork out an external process.
@ -11,7 +11,7 @@ import org.broadinstitute.sting.utils.Utils
class InProcessRunner(val function: InProcessFunction) extends JobRunner[InProcessFunction] {
private var runStatus: RunnerStatus.Value = _
def start() = {
def start() {
getRunInfo.startTime = new Date()
getRunInfo.exechosts = Utils.resolveHostname()
runStatus = RunnerStatus.RUNNING
@ -20,7 +20,7 @@ class InProcessRunner(val function: InProcessFunction) extends JobRunner[InProce
getRunInfo.doneTime = new Date()
val content = "%s%nDone.".format(function.description)
IOUtils.writeContents(function.jobOutputFile, content)
FileUtils.writeStringToFile(function.jobOutputFile, content)
runStatus = RunnerStatus.DONE
}

View File

@ -40,6 +40,7 @@ import collection.immutable.{TreeSet, TreeMap}
import org.broadinstitute.sting.queue.function.scattergather.{ScatterFunction, CloneFunction, GatherFunction, ScatterGatherableFunction}
import java.util.Date
import org.broadinstitute.sting.utils.Utils
import org.broadinstitute.sting.utils.io.IOUtils
/**
* The internal dependency tracker between sets of function input and output files.
@ -416,8 +417,12 @@ class QGraph extends Logging {
startedJobsToEmail = Set.empty[FunctionEdge]
}
if (readyJobs.size == 0 && runningJobs.size > 0)
Thread.sleep(nextRunningCheck(lastRunningCheck))
if (readyJobs.size == 0 && runningJobs.size > 0) {
runningLock.synchronized {
if (running)
runningLock.wait(nextRunningCheck(lastRunningCheck))
}
}
lastRunningCheck = System.currentTimeMillis
updateStatus()
@ -1002,7 +1007,12 @@ class QGraph extends Logging {
true
} else {
!this.jobGraph.edgeSet.exists(edge => {
edge.isInstanceOf[FunctionEdge] && edge.asInstanceOf[FunctionEdge].status == RunnerStatus.FAILED
if (edge.isInstanceOf[FunctionEdge]) {
val status = edge.asInstanceOf[FunctionEdge].status
(status == RunnerStatus.PENDING || status == RunnerStatus.RUNNING || status == RunnerStatus.FAILED)
} else {
false
}
})
}
}
@ -1051,7 +1061,13 @@ class QGraph extends Logging {
def shutdown() {
// Signal the main thread to shutdown.
running = false
// Wait for the thread to finish and exit normally.
// Try and wait for the thread to finish and exit normally.
runningLock.synchronized {
runningLock.notify()
}
// Start killing jobs.
runningLock.synchronized {
val runners = runningJobs.map(_.runner)
runningJobs = Set.empty[FunctionEdge]

View File

@ -56,6 +56,6 @@ class DrmaaJobManager extends CommandLineJobManager[DrmaaJobRunner] {
updatedRunners
}
override def tryStop(runners: Set[DrmaaJobRunner]) {
runners.filterNot(_.jobId == null).foreach(_.tryStop())
runners.foreach(_.tryStop())
}
}

View File

@ -135,14 +135,18 @@ class DrmaaJobRunner(val session: Session, val function: CommandLineFunction) ex
def tryStop() {
session.synchronized {
try {
// Stop runners. SIGTERM(15) is preferred to SIGKILL(9).
// Only way to send SIGTERM is for the Sys Admin set the terminate_method
// resource of the designated queue to SIGTERM
session.control(jobId, Session.TERMINATE)
} catch {
case e =>
logger.error("Unable to kill job " + jobId, e)
// Assumes that after being set the job may be
// reassigned but will not be reset back to null
if (jobId != null) {
try {
// Stop runners. SIGTERM(15) is preferred to SIGKILL(9).
// Only way to send SIGTERM is for the Sys Admin set the terminate_method
// resource of the designated queue to SIGTERM
session.control(jobId, Session.TERMINATE)
} catch {
case e =>
logger.error("Unable to kill job " + jobId, e)
}
}
}
}

View File

@ -30,4 +30,5 @@ import org.broadinstitute.sting.queue.engine.CommandLineJobManager
class ShellJobManager extends CommandLineJobManager[ShellJobRunner] {
def runnerType = classOf[ShellJobRunner]
def create(function: CommandLineFunction) = new ShellJobRunner(function)
override def tryStop(runners: Set[ShellJobRunner]) { runners.foreach(_.tryStop()) }
}

View File

@ -25,41 +25,66 @@
package org.broadinstitute.sting.queue.engine.shell
import org.broadinstitute.sting.queue.function.CommandLineFunction
import org.broadinstitute.sting.queue.util.ShellJob
import org.broadinstitute.sting.queue.engine.{RunnerStatus, CommandLineJobRunner}
import java.util.Date
import org.broadinstitute.sting.gatk.phonehome.GATKRunReport
import org.broadinstitute.sting.utils.Utils
import org.broadinstitute.sting.utils.runtime.{ProcessSettings, OutputStreamSettings, ProcessController}
/**
* Runs jobs one at a time locally
*/
class ShellJobRunner(val function: CommandLineFunction) extends CommandLineJobRunner {
private var runStatus: RunnerStatus.Value = _
// Controller on the thread that started the job
private var controller: ProcessController = null
/**
* Runs the function on the local shell.
* @param function Command to run.
*/
def start() {
val job = new ShellJob
val commandLine = Array("sh", jobScript.getAbsolutePath)
val stdoutSettings = new OutputStreamSettings
val stderrSettings = new OutputStreamSettings
val mergeError = (function.jobErrorFile != null)
job.workingDir = function.commandDirectory
job.outputFile = function.jobOutputFile
job.errorFile = function.jobErrorFile
stdoutSettings.setOutputFile(function.jobOutputFile, true)
if (function.jobErrorFile != null)
stderrSettings.setOutputFile(function.jobErrorFile, true)
job.shellScript = jobScript
if (logger.isDebugEnabled) {
stdoutSettings.printStandard(true)
stderrSettings.printStandard(true)
}
// Allow advanced users to update the job.
updateJobRun(job)
val processSettings = new ProcessSettings(
commandLine, mergeError, function.commandDirectory, null,
null, stdoutSettings, stderrSettings)
updateJobRun(processSettings)
getRunInfo.startTime = new Date()
getRunInfo.exechosts = Utils.resolveHostname()
updateStatus(RunnerStatus.RUNNING)
job.run()
controller = ProcessController.getThreadLocal
val exitStatus = controller.exec(processSettings).getExitValue
getRunInfo.doneTime = new Date()
updateStatus(RunnerStatus.DONE)
updateStatus(if (exitStatus == 0) RunnerStatus.DONE else RunnerStatus.FAILED)
}
override def checkUnknownStatus() {}
/**
* Possibly invoked from a shutdown thread, find and
* stop the controller from the originating thread
*/
def tryStop() {
// Assumes that after being set the job may be
// reassigned but will not be reset back to null
if (controller != null) {
try {
controller.tryDestroy()
} catch {
case e =>
logger.error("Unable to kill shell job: " + function.description)
}
}
}
}

View File

@ -27,7 +27,7 @@ package org.broadinstitute.sting.queue.extensions.gatk
import org.broadinstitute.sting.utils.interval.IntervalUtils
import java.io.File
import collection.JavaConversions._
import org.broadinstitute.sting.queue.util.IOUtils
import org.broadinstitute.sting.utils.io.IOUtils
import org.broadinstitute.sting.queue.function.scattergather.{CloneFunction, ScatterFunction}
import org.broadinstitute.sting.commandline.Output

View File

@ -1,7 +1,7 @@
package org.broadinstitute.sting.queue.extensions.gatk
import java.io.File
import org.broadinstitute.sting.queue.util.FileExtension
import org.broadinstitute.sting.utils.io.FileExtension
import java.lang.String
/**

View File

@ -1,7 +1,7 @@
package org.broadinstitute.sting.queue.extensions.gatk
import java.io.File
import org.broadinstitute.sting.queue.util.FileExtension
import org.broadinstitute.sting.utils.io.FileExtension
/**
* Used to provide tagged -I input_file arguments to the GATK.

View File

@ -25,7 +25,7 @@
package org.broadinstitute.sting.queue.function
import org.broadinstitute.sting.commandline.Argument
import org.broadinstitute.sting.queue.util.IOUtils
import org.broadinstitute.sting.utils.io.IOUtils
import java.io.File
/**

View File

@ -31,6 +31,7 @@ import org.broadinstitute.sting.queue.{QException, QSettings}
import collection.JavaConversions._
import org.broadinstitute.sting.queue.function.scattergather.SimpleTextGatherFunction
import org.broadinstitute.sting.queue.util._
import org.broadinstitute.sting.utils.io.IOUtils
/**
* The base interface for all functions in Queue.

View File

@ -4,7 +4,8 @@ import java.io.File
import org.broadinstitute.sting.commandline.{Input, Output}
import org.broadinstitute.sting.queue.function.QFunction
import org.broadinstitute.sting.queue.QException
import org.broadinstitute.sting.queue.util.IOUtils
import org.broadinstitute.sting.utils.io.IOUtils
import collection.JavaConversions._
/**
* Base class for Gather command line functions.
@ -29,7 +30,7 @@ trait GatherFunction extends QFunction {
/**
* Waits for gather parts to propagate over NFS or throws an exception.
*/
protected def waitForGatherParts = {
protected def waitForGatherParts() {
val missing = IOUtils.waitFor(gatherParts, 120)
if (!missing.isEmpty)
throw new QException("Unable to find gather inputs: " + missing.mkString(", "))

View File

@ -29,6 +29,7 @@ import org.broadinstitute.sting.queue.util._
import org.broadinstitute.sting.commandline.{Gatherer, Gather, ArgumentSource}
import org.broadinstitute.sting.queue.function.{QFunction, CommandLineFunction}
import org.broadinstitute.sting.queue.QException
import org.broadinstitute.sting.utils.io.IOUtils
/**
* A function that can be run faster by splitting it up into pieces and then joining together the results.
@ -82,8 +83,8 @@ trait ScatterGatherableFunction extends CommandLineFunction {
/**
* Sets the scatter gather directory to the command directory if it is not already set.
*/
override def freezeFieldValues = {
super.freezeFieldValues
override def freezeFieldValues() {
super.freezeFieldValues()
if (this.scatterGatherDirectory == null) {
if (qSettings.jobScatterGatherDirectory != null) {
@ -98,10 +99,14 @@ trait ScatterGatherableFunction extends CommandLineFunction {
* The scatter function.
*/
private lazy val scatterFunction = {
// Only depend on input fields that have a value
val inputFieldsWithValues = this.inputFields.filter(hasFieldValue(_))
val inputFiles = inputFieldsWithValues.flatMap(getFieldFiles(_)).toSet
val scatterFunction = newScatterFunction()
this.copySettingsTo(scatterFunction)
scatterFunction.originalFunction = this
scatterFunction.originalInputs = this.inputs
scatterFunction.originalInputs = inputFiles
scatterFunction.commandDirectory = this.scatterGatherTempDir("scatter")
scatterFunction.isIntermediate = true
scatterFunction.addOrder = this.addOrder :+ 1
@ -121,8 +126,6 @@ trait ScatterGatherableFunction extends CommandLineFunction {
def generateFunctions() = {
var functions = List.empty[QFunction]
// Only depend on input fields that have a value
val inputFieldsWithValues = this.inputFields.filter(hasFieldValue(_))
// Only gather up fields that will have a value
val outputFieldsWithValues = this.outputFields.filter(hasFieldValue(_))
@ -228,7 +231,7 @@ trait ScatterGatherableFunction extends CommandLineFunction {
* Calls setupScatterFunction with scatterFunction.
* @param scatterFunction The function that will create the scatter pieces in the temporary directories.
*/
protected def initScatterFunction(scatterFunction: ScatterFunction) = {
protected def initScatterFunction(scatterFunction: ScatterFunction) {
if (this.setupScatterFunction != null)
if (this.setupScatterFunction.isDefinedAt(scatterFunction))
this.setupScatterFunction(scatterFunction)
@ -272,7 +275,7 @@ trait ScatterGatherableFunction extends CommandLineFunction {
* @param gatherFunction The function that will merge the gather pieces from the temporary directories.
* @param gatherField The output field being gathered.
*/
protected def initGatherFunction(gatherFunction: GatherFunction, gatherField: ArgumentSource) = {
protected def initGatherFunction(gatherFunction: GatherFunction, gatherField: ArgumentSource) {
if (this.setupGatherFunction != null)
if (this.setupGatherFunction.isDefinedAt(gatherFunction, gatherField))
this.setupGatherFunction(gatherFunction, gatherField)
@ -289,7 +292,7 @@ trait ScatterGatherableFunction extends CommandLineFunction {
* @param cloneFunction The clone of this ScatterGatherableFunction
* @param index The one based index (from 1..scatterCount inclusive) of the scatter piece.
*/
protected def initCloneFunction(cloneFunction: CloneFunction, index: Int) = {
protected def initCloneFunction(cloneFunction: CloneFunction, index: Int) {
if (this.setupCloneFunction != null)
if (this.setupCloneFunction.isDefinedAt(cloneFunction, index))
this.setupCloneFunction(cloneFunction, index)

View File

@ -1,51 +0,0 @@
package org.broadinstitute.sting.queue.util
import java.io.File
/**
* Base utility class for a command line job.
*/
abstract class CommandLineJob {
var shellScript: File = _
var workingDir: File = _
var inputFile: File = _
var outputFile: File = _
var errorFile: File = _
/**
* Runs the command, either immediately or dispatching it to a compute farm.
* If it is dispatched to a compute farm it should not start until jobs it depends on are finished.
*/
def run()
/**
* Returns the content of a command output.
* @param streamOutput The output of the command.
* @return The content of the command, along with a message if it was truncated.
*/
protected def content(streamOutput: ProcessController.StreamOutput) = {
var content = streamOutput.content
if (streamOutput.contentTruncated)
content += "%n%n<truncated>".format()
content
}
/**
* Returns the ProcessController for this thread.
* @return The ProcessController for this thread.
*/
protected def processController = CommandLineJob.threadProcessController.get
/** A five mb limit of characters for display. */
protected val FIVE_MB = 1024 * 512 * 5;
}
/**
* Base class for a command line job.
*/
object CommandLineJob {
/** Thread local process controller container. */
private val threadProcessController = new ThreadLocal[ProcessController] {
override def initialValue = new ProcessController
}
}

View File

@ -1,15 +0,0 @@
package org.broadinstitute.sting.queue.util
import java.io.File
/**
* An trait for @Input or @Output CommandLineFunction fields that are extensions of files.
*/
trait FileExtension extends File {
/**
* Returns a clone of the FileExtension with the new path.
* @param newPath new path for the clone of this FileExtension
* @return a clone of the FileExtension with the new path.
*/
def withPath(newPath: String): File
}

View File

@ -1,253 +0,0 @@
package org.broadinstitute.sting.queue.util
import org.apache.commons.io.FileUtils
import java.io.{FileReader, File}
import org.broadinstitute.sting.utils.exceptions.UserException
import org.broadinstitute.sting.queue.QException
/**
* A collection of utilities for modifying java.io.
*/
object IOUtils extends Logging {
/**
* Checks if the temp directory has been setup and throws an exception if they user hasn't set it correctly.
* @param tempDir Temporary directory.
*/
def checkTempDir(tempDir: File) {
val tempDirPath = tempDir.getAbsolutePath
// Keeps the user from leaving the temp directory as the default, and on Macs from having pluses
// in the path which can cause problems with the Google Reflections library.
// see also: http://benjchristensen.com/2009/09/22/mac-osx-10-6-java-java-io-tmpdir/
if (tempDirPath.startsWith("/var/folders/") || (tempDirPath == "/tmp") || (tempDirPath == "/tmp/"))
throw new UserException.BadTmpDir("java.io.tmpdir must be explicitly set")
if (!tempDir.exists && !tempDir.mkdirs)
throw new UserException.BadTmpDir("Could not create directory: " + tempDir.getAbsolutePath)
}
/**
* Creates a temp directory with the prefix and optional suffix.
* @param prefix Prefix for the directory name.
* @param suffix Optional suffix for the directory name.
* @param tempDirParent Parent directory for the temp directory.
* @return The created temporary directory.
*/
def tempDir(prefix: String, suffix: String = "", tempDirParent: File) = {
if (!tempDirParent.exists && !tempDirParent.mkdirs)
throw new UserException.BadTmpDir("Could not create temp directory: " + tempDirParent)
val temp = File.createTempFile(prefix + "-", suffix, tempDirParent)
if (!temp.delete)
throw new UserException.BadTmpDir("Could not delete sub file: " + temp.getAbsolutePath)
if (!temp.mkdir)
throw new UserException.BadTmpDir("Could not create sub directory: " + temp.getAbsolutePath)
absolute(temp)
}
/**
* Writes content into a file.
* @param file File to write to.
* @param content Content to write.
*/
def writeContents(file: File, content: String) { FileUtils.writeStringToFile(file, content) }
/**
* Reads content of a file into a string.
* Only for use on really small files!
* @param file File to read to.
* @return content Content of the file.
*/
def readContents(file: File) = FileUtils.readFileToString(file)
/**
* Writes content to a temp file and returns the path to the temporary file.
* @param content to write.
* @param prefix Prefix for the temp file.
* @param suffix Suffix for the temp file.
* @param directory Directory for the temp file.
* @return the path to the temp file.
*/
def writeTempFile(content: String, prefix: String, suffix: String, directory: File) = {
val tempFile = absolute(File.createTempFile(prefix, suffix, directory))
writeContents(tempFile, content)
tempFile
}
/**
* Waits for NFS to propagate a file creation, imposing a timeout.
*
* Based on Apache Commons IO FileUtils.waitFor()
*
* @param file The file to wait for.
* @param seconds The maximum time in seconds to wait.
* @return true if the file exists
*/
def waitFor(file: File, seconds: Int): Boolean = waitFor(List(file), seconds).isEmpty
/**
* Waits for NFS to propagate a file creation, imposing a timeout.
*
* Based on Apache Commons IO FileUtils.waitFor()
*
* @param files The list of files to wait for.
* @param seconds The maximum time in seconds to wait.
* @return Files that still do not exists at the end of the timeout, or a empty list if all files exists.
*/
def waitFor[T <: Traversable[File]](files: T, seconds: Int): Traversable[File] = {
var timeout = 0;
var tick = 0;
var missingFiles = files.filterNot(_.exists)
while (!missingFiles.isEmpty && timeout <= seconds) {
if (tick >= 10) {
tick = 0;
timeout += 1
}
tick += 1
try {
Thread.sleep(100)
} catch {
case ignore: InterruptedException =>
}
missingFiles = missingFiles.filterNot(_.exists)
}
missingFiles
}
/**
* Returns the directory at the number of levels deep.
* For example 2 levels of /path/to/dir will return /path/to
* @param dir Directory path.
* @param level how many levels deep from the root.
* @return The path to the parent directory that is level-levels deep.
*/
def dirLevel(dir: File, level: Int): File = {
var directories = List.empty[File]
var parentDir = absolute(dir)
while (parentDir != null) {
directories +:= parentDir
parentDir = parentDir.getParentFile
}
if (directories.size <= level)
directories.last
else
directories(level)
}
/**
* Returns the sub path rooted at the parent.
* @param parent The parent directory.
* @param path The sub path to append to the parent, if the path is not absolute.
* @return The absolute path to the file in the parent dir if the path was not absolute, otherwise the original path.
*/
def absolute(parent: File, path: String): File =
absolute(parent, new File(path))
/**
* Returns the sub path rooted at the parent.
* @param parent The parent directory.
* @param file The sub path to append to the parent, if the path is not absolute.
* @return The absolute path to the file in the parent dir if the path was not absolute, otherwise the original path.
*/
def absolute(parent: File, file: File): File = {
val newPath =
if (file.isAbsolute)
absolutePath(file)
else
absolutePath(new File(parent, file.getPath))
replacePath(file, newPath)
}
/**
* A mix of getCanonicalFile and getAbsoluteFile that returns the
* absolute path to the file without deferencing symbolic links.
* @param file the file.
* @return the absolute path to the file.
*/
def absolute(file: File) = {
replacePath(file, absolutePath(file))
}
private def absolutePath(file: File) = {
var fileAbs = file.getAbsoluteFile
var names = List.empty[String]
while (fileAbs != null) {
val name = fileAbs.getName
fileAbs = fileAbs.getParentFile
if (name == ".") {
/* skip */
/* TODO: What do we do for ".."?
} else if (name == "..") {
CentOS tcsh says use getCanonicalFile:
~ $ mkdir -p test1/test2
~ $ ln -s test1/test2 test3
~ $ cd test3/..
~/test1 $
Mac bash says keep going with getAbsoluteFile:
~ $ mkdir -p test1/test2
~ $ ln -s test1/test2 test3
~ $ cd test3/..
~ $
For now, leave it and let the shell figure it out.
*/
} else {
names +:= name
}
}
names.mkString("/", "/", "")
}
private def replacePath(file: File, path: String) = {
file match {
case fileExtension: FileExtension =>
fileExtension.withPath(path)
case file: File =>
if (file.getClass != classOf[File])
throw new QException("Sub classes of java.io.File must also implement FileExtension so that the path can be modified.")
new File(path)
}
}
/**
* Returns the last lines of the file.
* NOTE: This is only safe to run on smaller files!
* @param file File to read.
* @param count Maximum number of lines to return.
* @return The last count lines from file.
*/
def tail(file: File, count: Int) = {
var tailLines = List.empty[String]
var reader = new FileReader(file)
try {
val iterator = org.apache.commons.io.IOUtils.lineIterator(reader)
var lineCount = 0
while (iterator.hasNext) {
val line = iterator.nextLine
lineCount += 1
if (lineCount > count)
tailLines = tailLines.tail
tailLines :+= line
}
} finally {
org.apache.commons.io.IOUtils.closeQuietly(reader)
}
tailLines
}
/**
* Tries to delete a file. Emits a warning if the file was unable to be deleted.
* @param file File to delete.
* @return true if the file was deleted.
*/
def tryDelete(file: File) = {
val deleted = FileUtils.deleteQuietly(file)
if (deleted)
logger.debug("Deleted " + file)
else if (file.exists)
logger.warn("Unable to delete " + file)
deleted
}
}

View File

@ -1,11 +0,0 @@
package org.broadinstitute.sting.queue.util
import org.broadinstitute.sting.queue.QException
/**
* Captures the exit code and error text from a failed process.
*/
class JobExitException(val exitText: String, val commandLine: Array[String], val exitCode: Int, val stdErr: String)
extends QException("%s%nCommand line:%n%s%nExit code: %s%nStandard error contained: %n%s"
.format(exitText, commandLine.mkString(" "), exitCode, stdErr)) {
}

View File

@ -1,369 +0,0 @@
package org.broadinstitute.sting.queue.util
import java.io._
import scala.collection.mutable.{HashSet, ListMap}
/**
* Facade to Runtime.exec() and java.lang.Process. Handles
* running a process to completion and returns stdout and stderr
* as strings. Creates separate threads for reading stdout and stderr,
* then reuses those threads for each process most efficient use is
* to create one of these and use it repeatedly. Instances are not
* thread-safe, however.
*
* @author originally by Michael Koehrsen ported to scala and enhanced by Khalid Shakir
*/
class ProcessController extends Logging {
// Threads that capture stdout and stderr
private val stdoutCapture = new OutputCapture(ProcessController.STDOUT_KEY)
private val stderrCapture = new OutputCapture(ProcessController.STDERR_KEY)
// Communication channels with output capture threads
/** Holds the stdout and stderr sent to the background capture threads */
private val toCapture = new ListMap[String, ProcessController.CapturedStreamOutput]
/** Holds the results of the capture from the background capture threads.
* May be the content via toCapture or an EmptyStreamOutput if the capture was interrupted. */
private val fromCapture = new ListMap[String, ProcessController.StreamOutput]
// Start the background threads for this controller.
stdoutCapture.start()
stderrCapture.start()
/**
* Executes a command line program with the settings and waits for it to return, processing the output on a background thread.
* @param settings Settings to be run.
* @return The output of the command.
*/
def exec(settings: ProcessController.ProcessSettings): ProcessController.ProcessOutput = {
var builder = new ProcessBuilder(settings.cmdarray:_*)
builder.directory(settings.directory)
if (settings.environment != null) {
val builderEnvironment = builder.environment
builderEnvironment.clear()
settings.environment.foreach{case (name, value) => builderEnvironment.put(name, value)}
}
builder.redirectErrorStream(settings.redirectErrorStream)
var stdout: ProcessController.StreamOutput = null
var stderr: ProcessController.StreamOutput = null
val process = builder.start
ProcessController.running.add(process)
try {
val stdoutSettings = if (settings.stdoutSettings == null) ProcessController.EmptyStreamSettings else settings.stdoutSettings
val stderrSettings = if (settings.stderrSettings == null) ProcessController.EmptyStreamSettings else settings.stderrSettings
toCapture.synchronized {
toCapture.put(ProcessController.STDOUT_KEY, new ProcessController.CapturedStreamOutput(process.getInputStream, stdoutSettings, scala.Console.out))
toCapture.put(ProcessController.STDERR_KEY, new ProcessController.CapturedStreamOutput(process.getErrorStream, stderrSettings, scala.Console.err))
toCapture.notifyAll()
}
if (settings.stdinSettings.input != null) {
val writer = new OutputStreamWriter(process.getOutputStream)
writer.write(settings.stdinSettings.input)
writer.flush()
}
if (settings.stdinSettings.inputFile != null) {
val reader = new FileReader(settings.stdinSettings.inputFile)
val writer = new OutputStreamWriter(process.getOutputStream)
val buf = new Array[Char](4096)
var readCount = 0
while ({readCount = reader.read(buf); readCount} >= 0)
writer.write(buf, 0, readCount)
writer.flush()
reader.close()
}
try {
process.getOutputStream.close()
process.waitFor()
} finally {
while (stdout == null || stderr == null) {
fromCapture.synchronized {
fromCapture.remove(ProcessController.STDOUT_KEY) match {
case Some(stream) => stdout = stream
case None => /* ignore */
}
fromCapture.remove(ProcessController.STDERR_KEY) match {
case Some(stream) => stderr = stream
case None => /* ignore */
}
try {
if (stdout == null || stderr == null)
fromCapture.wait()
} catch {
case e: InterruptedException =>
logger.error(e)
}
}
}
}
} finally {
ProcessController.running.remove(process)
}
new ProcessController.ProcessOutput(process.exitValue, stdout, stderr)
}
/** Ensures that the threads used to manipulate the IO for the process are cleaned up properly. */
def close() = {
try {
stdoutCapture.interrupt()
stderrCapture.interrupt()
} catch {
case e =>
logger.error(e)
}
}
/** calls close() */
override def finalize = close()
/**
* Reads in the output of a stream on a background thread to keep the output pipe from backing up and freezing the called process.
* @param key The stdout or stderr key for this output capture.
*/
private class OutputCapture(private val key: String)
extends Thread("OutputCapture-" + key + "-" + Thread.currentThread.getName) {
setDaemon(true)
/** Runs the capture. */
override def run = {
var break = false
while (!break) {
var processStream: ProcessController.StreamOutput = ProcessController.EmptyStreamOutput
try {
// Wait for a new input stream to be passed from this process controller.
var capturedProcessStream: ProcessController.CapturedStreamOutput = null
while (capturedProcessStream == null) {
toCapture.synchronized {
toCapture.remove(key) match {
case Some(stream) => capturedProcessStream = stream
case None => toCapture.wait()
}
}
}
// Read in the input stream
processStream = capturedProcessStream
capturedProcessStream.read
} catch {
case e: InterruptedException => {
logger.info("OutputReader interrupted, exiting")
break = true
}
case e: IOException => {
logger.error("Error reading process output", e)
}
} finally {
// Send the string back to the process controller.
fromCapture.synchronized {
fromCapture.put(key, processStream)
fromCapture.notify()
}
}
}
}
}
}
/**
* Facade to Runtime.exec() and java.lang.Process. Handles
* running a process to completion and returns stdout and stderr
* as strings. Creates separate threads for reading stdout and stderr,
* then reuses those threads for each process most efficient use is
* to create one of these and use it repeatedly. Instances are not
* thread-safe, however.
*
* @author originally by Michael Koehrsen ported to scala and enhanced by Khalid Shakir
*/
object ProcessController extends Logging {
/**
* Settings that define how to run a process.
* @param cmdarray Command line to run.
* @param environment Environment settings to override System.getEnv, or null to use System.getEnv.
* @param directory The directory to run the command in, or null to run in the current directory.
* @param stdinSettings Settings for writing to the process stdin.
* @param stdoutSettings Settings for capturing the process stdout.
* @param stderrSettings Setting for capturing the process stderr.
* @param redirectErrorStream true if stderr should be sent to stdout.
*/
class ProcessSettings(val cmdarray: Array[String], val environment: Map[String, String], val directory: File,
val stdinSettings: InputStreamSettings, val stdoutSettings: OutputStreamSettings,
val stderrSettings: OutputStreamSettings, val redirectErrorStream: Boolean)
/**
* Settings that define text to write to the process stdin.
* @param input String to write to stdin.
* @param inputFile File to write to stdin.
*/
class InputStreamSettings(val input: String, val inputFile: File)
/**
* Settings that define text to capture from a process stream.
* @param stringSize The number of characters to capture, or -1 for unlimited.
* @param outputFile The file to write output to, or null to skip output.
* @param outputFileAppend true if the output file should be appended to.
*/
class OutputStreamSettings(val stringSize: Int, val outputFile: File, val outputFileAppend: Boolean)
/**
* The output of a process.
* @param exitValue The exit value.
* @param stdout The capture of stdout as defined by the stdout OutputStreamSettings.
* @param stderr The capture of stderr as defined by the stderr OutputStreamSettings.
*/
class ProcessOutput(val exitValue: Int, val stdout: StreamOutput, val stderr: StreamOutput)
/**
* The base class of stream output.
*/
abstract class StreamOutput {
/**
* Returns the content as a string.
* @return The content as a string.
*/
def content: String
/**
* Returns true if the content was truncated.
* @return true if the content was truncated.
*/
def contentTruncated: Boolean
}
private var currentCaptureId = 0
/**
* Returns the next output capture id.
* @return The next output capture id.
*/
private def NEXT_OUTPUT_CAPTURE_ID = {
currentCaptureId += 1
currentCaptureId
}
private val STDOUT_KEY = "stdout"
private val STDERR_KEY = "stderr"
/** Tracks running processes so that they can be killed as the JVM shuts down. */
private val running = new HashSet[Process]
def shutdown() = {
for (process <- running.clone) {
logger.warn("Killing: " + process)
try {
process.destroy
} catch {
case _ => /* ignore */
}
}
}
/** Empty stream settings used when no output is requested. */
private object EmptyStreamSettings extends OutputStreamSettings(0, null, false)
/** Empty stream output when no output is captured due to an error. */
private object EmptyStreamOutput extends StreamOutput {
def content = ""
def contentTruncated = false
}
/**
* Stream output captured from a stream.
* @param stream Stream to capture output.
* @param settings Settings that define what to capture.
*/
private class CapturedStreamOutput(val stream: InputStream, val settings: OutputStreamSettings, val debugStream: PrintStream) extends StreamOutput {
/**
* Returns the captured content as a string.
* @return The captured content as a string.
*/
def content = stringWriter.toString()
/**
* Returns true if the captured content was truncated.
* @return true if the captured content was truncated.
*/
def contentTruncated = stringTruncated
/**
* Drain the input stream to keep the process from backing up until it's empty.
*/
def read() = {
val reader = new InputStreamReader(stream)
val buf = new Array[Char](4096)
var readCount = 0
while ({readCount = reader.read(buf); readCount} >= 0) {
writeString(buf, readCount)
writeFile(buf, readCount)
}
closeFile()
stream.close()
}
/** The string to write capture content. */
private lazy val stringWriter = if (settings.stringSize < 0) new StringWriter else new StringWriter(settings.stringSize)
/** True if the content is truncated. */
private var stringTruncated = false
/** The number of characters left until the buffer is full. */
private var stringRemaining = settings.stringSize
/**
* Writes the buffer to the stringWriter up to stringRemaining characters.
* @param chars Character buffer to write.
* @param len Number of characters in the buffer.
*/
private def writeString(chars: Array[Char], len: Int) = {
// If debug is enabled bypass the logger and dump directly to the screen
if (logger.isDebugEnabled)
debugStream.print(new String(chars, 0, len))
if (settings.stringSize < 0) {
stringWriter.write(chars, 0, len)
} else {
if (!stringTruncated) {
stringWriter.write(chars, 0, if (len > stringRemaining) stringRemaining else len)
stringRemaining -= len
if (stringRemaining < 0)
stringTruncated = true
}
}
}
/** The file writer to capture content or null if no output file was requested. */
private lazy val fileWriter = {
if (settings.outputFile == null) {
null
} else {
new FileWriter(settings.outputFile, settings.outputFileAppend)
}
}
/**
* Writes the buffer to the fileWriter if it is not null.
* @param chars Character buffer to write.
* @param len Number of characters in the buffer.
*/
private def writeFile(chars: Array[Char], len: Int) = {
if (fileWriter != null) {
fileWriter.write(chars, 0, len)
fileWriter.flush()
}
}
/** Closes the fileWriter if it is not null. */
private def closeFile() = {
if (fileWriter != null) {
fileWriter.flush
fileWriter.close
}
}
}
}

View File

@ -23,15 +23,15 @@
*/
package org.broadinstitute.sting.queue.util
import org.broadinstitute.sting.queue.function.QFunction
import org.broadinstitute.sting.gatk.report.{GATKReportTable, GATKReport}
import org.broadinstitute.sting.utils.exceptions.UserException
import org.broadinstitute.sting.queue.engine.JobRunInfo
import java.io.{FileOutputStream, PrintStream, File}
import org.broadinstitute.sting.queue.function.scattergather.{GathererFunction, ScatterFunction}
import org.broadinstitute.sting.utils.R.RScriptExecutor.RScriptArgumentCollection
import org.broadinstitute.sting.utils.R.RScriptExecutor
import org.broadinstitute.sting.queue.QScript
import org.broadinstitute.sting.utils.R.{RScriptLibrary, RScriptExecutor}
import org.broadinstitute.sting.utils.io.Resource
/**
* A mixin to add Job info to the class
@ -104,10 +104,12 @@ object QJobReport {
stream.close()
}
def plotReport(args: RScriptArgumentCollection, jobReportFile: File) {
def plotReport(args: RScriptArgumentCollection, reportFile: File, pdfFile: File) {
val executor = new RScriptExecutor(args, false) // don't except on error
val pdf = jobReportFile.getAbsolutePath + ".pdf"
executor.callRScripts(JOB_REPORT_QUEUE_SCRIPT, jobReportFile.getAbsolutePath, pdf)
executor.addLibrary(RScriptLibrary.GSALIB)
executor.addScript(new Resource(JOB_REPORT_QUEUE_SCRIPT, classOf[QJobReport]))
executor.addArgs(reportFile.getAbsolutePath, pdfFile.getAbsolutePath)
executor.exec()
}
def workAroundSameJobNames(func: QFunction):String = {

View File

@ -1,27 +0,0 @@
package org.broadinstitute.sting.queue.util
/**
* Runs a job on the command line by invoking "sh -c <command>"
*/
class ShellJob extends CommandLineJob with Logging {
/**
* Runs the command and waits for the output.
*/
def run() = {
val (redirectError, errorFile) = if (this.errorFile == null) (true, null) else (false, this.errorFile)
val bufferSize = if (redirectError || logger.isDebugEnabled) FIVE_MB else 0
val stdinSettings = new ProcessController.InputStreamSettings(null, this.inputFile)
val stdoutSettings = new ProcessController.OutputStreamSettings(bufferSize, this.outputFile, true)
val stderrSettings = new ProcessController.OutputStreamSettings(FIVE_MB, errorFile, true)
val commandLine = Array("sh", shellScript.toString)
val processSettings = new ProcessController.ProcessSettings(
commandLine, null, this.workingDir, stdinSettings, stdoutSettings, stderrSettings, redirectError)
val output = processController.exec(processSettings)
if (output.exitValue != 0) {
val streamOutput = if (redirectError) output.stdout else output.stderr
throw new JobExitException("Failed to run job.", commandLine, output.exitValue, content(streamOutput))
}
}
}

View File

@ -33,7 +33,7 @@ import java.text.SimpleDateFormat
import org.broadinstitute.sting.BaseTest
import org.broadinstitute.sting.MD5DB
import org.broadinstitute.sting.queue.QCommandLine
import org.broadinstitute.sting.queue.util.{Logging, ProcessController}
import org.broadinstitute.sting.queue.util.Logging
import java.io.File
import org.broadinstitute.sting.gatk.report.GATKReport
import org.apache.commons.io.FileUtils
@ -217,11 +217,6 @@ object PipelineTest extends BaseTest with Logging {
Runtime.getRuntime.addShutdownHook(new Thread {
/** Cleanup as the JVM shuts down. */
override def run() {
try {
ProcessController.shutdown()
} catch {
case _ => /*ignore */
}
runningCommandLines.foreach(commandLine =>
try {
commandLine.shutdown()

View File

@ -1,122 +0,0 @@
package org.broadinstitute.sting.queue.util
import org.broadinstitute.sting.BaseTest
import java.io.File
import org.broadinstitute.sting.utils.exceptions.UserException
import org.testng.Assert
import org.testng.annotations.Test
class IOUtilsUnitTest extends BaseTest {
@Test
def testGoodTempDir = {
IOUtils.checkTempDir(new File("/tmp/queue"))
}
@Test(expectedExceptions=Array(classOf[UserException.BadTmpDir]))
def testBadTempDir = {
IOUtils.checkTempDir(new File("/tmp"))
}
@Test
def testAbsoluteSubDir = {
var subDir = IOUtils.absolute(new File("."), new File("/path/to/file"))
Assert.assertEquals(subDir, new File("/path/to/file"))
subDir = IOUtils.absolute(new File("/different/path"), new File("/path/to/file"))
Assert.assertEquals(subDir, new File("/path/to/file"))
subDir = IOUtils.absolute(new File("/different/path"), new File("."))
Assert.assertEquals(subDir, new File("/different/path"))
}
@Test
def testRelativeSubDir = {
var subDir = IOUtils.absolute(new File("."), new File("path/to/file"))
Assert.assertEquals(subDir.getCanonicalFile, new File("path/to/file").getCanonicalFile)
subDir = IOUtils.absolute(new File("/different/path"), new File("path/to/file"))
Assert.assertEquals(subDir, new File("/different/path/path/to/file"))
}
@Test
def testDottedSubDir = {
var subDir = IOUtils.absolute(new File("."), new File("path/../to/file"))
Assert.assertEquals(subDir.getCanonicalFile, new File("path/../to/./file").getCanonicalFile)
subDir = IOUtils.absolute(new File("."), new File("/path/../to/file"))
Assert.assertEquals(subDir, new File("/path/../to/file"))
subDir = IOUtils.absolute(new File("/different/../path"), new File("path/to/file"))
Assert.assertEquals(subDir, new File("/different/../path/path/to/file"))
subDir = IOUtils.absolute(new File("/different/./path"), new File("/path/../to/file"))
Assert.assertEquals(subDir, new File("/path/../to/file"))
}
@Test
def testTempDir = {
val tempDir = IOUtils.tempDir("Q-Unit-Test", "", new File("queueTempDirToDelete"))
Assert.assertTrue(tempDir.exists)
Assert.assertFalse(tempDir.isFile)
Assert.assertTrue(tempDir.isDirectory)
val deleted = IOUtils.tryDelete(tempDir)
Assert.assertTrue(deleted)
Assert.assertFalse(tempDir.exists)
}
@Test
def testDirLevel = {
var dir = IOUtils.dirLevel(new File("/path/to/directory"), 1)
Assert.assertEquals(dir, new File("/path"))
dir = IOUtils.dirLevel(new File("/path/to/directory"), 2)
Assert.assertEquals(dir, new File("/path/to"))
dir = IOUtils.dirLevel(new File("/path/to/directory"), 3)
Assert.assertEquals(dir, new File("/path/to/directory"))
dir = IOUtils.dirLevel(new File("/path/to/directory"), 4)
Assert.assertEquals(dir, new File("/path/to/directory"))
}
@Test
def testAbsolute = {
var dir = IOUtils.absolute(new File("/path/./to/./directory/."))
Assert.assertEquals(dir, new File("/path/to/directory"))
dir = IOUtils.absolute(new File("/"))
Assert.assertEquals(dir, new File("/"))
dir = IOUtils.absolute(new File("/."))
Assert.assertEquals(dir, new File("/"))
dir = IOUtils.absolute(new File("/././."))
Assert.assertEquals(dir, new File("/"))
dir = IOUtils.absolute(new File("/./directory/."))
Assert.assertEquals(dir, new File("/directory"))
dir = IOUtils.absolute(new File("/./directory/./"))
Assert.assertEquals(dir, new File("/directory"))
dir = IOUtils.absolute(new File("/./directory./"))
Assert.assertEquals(dir, new File("/directory."))
dir = IOUtils.absolute(new File("/./.directory/"))
Assert.assertEquals(dir, new File("/.directory"))
}
@Test
def testTail = {
val lines = List(
"chr18_random 4262 3154410390 50 51",
"chr19_random 301858 3154414752 50 51",
"chr21_random 1679693 3154722662 50 51",
"chr22_random 257318 3156435963 50 51",
"chrX_random 1719168 3156698441 50 51")
val tail = IOUtils.tail(new File(BaseTest.hg18Reference + ".fai"), 5)
Assert.assertEquals(tail.size, 5)
for (i <- 0 until 5)
Assert.assertEquals(tail(i), lines(i))
}
}

View File

@ -1,73 +0,0 @@
package org.broadinstitute.sting.queue.util
import org.broadinstitute.sting.BaseTest
import org.testng.annotations.Test
import org.testng.Assert
class ShellJobUnitTest {
@Test
def testEcho {
val job = new ShellJob
job.shellScript = writeScript("echo Hello World")
job.run()
}
@Test(expectedExceptions=Array(classOf[JobExitException]))
def testBadQuotes {
val job = new ShellJob
job.shellScript = writeScript("echo 'Hello World")
job.run()
}
@Test
def testGoodQuotes {
val job = new ShellJob
job.shellScript = writeScript("echo 'Hello World'")
job.run()
}
@Test
def testEscapeCharacters {
var job: ShellJob = null
job = new ShellJob
job.shellScript = writeScript("echo #")
job.outputFile = BaseTest.createTempFile("temp", "")
job.run()
Assert.assertEquals(IOUtils.readContents(job.outputFile).trim, "")
job = new ShellJob
job.shellScript = writeScript("""echo \#""")
job.outputFile = BaseTest.createTempFile("temp", "")
job.run()
Assert.assertEquals(IOUtils.readContents(job.outputFile).trim, "#")
job = new ShellJob
job.shellScript = writeScript("""echo \\#""")
job.outputFile = BaseTest.createTempFile("temp", "")
job.run()
Assert.assertEquals(IOUtils.readContents(job.outputFile).trim, """\#""")
}
@Test
def testLongCommand {
// This command fails on some systems with a 4096 character limit when run via the old sh -c "echo ...",
// but works on the same systems when run via sh <script>
val builder = new StringBuilder
builder.append("echo ")
for (i <- 1 to 500) {
val s = i.toString
builder.append("000".take(3-s.length)).append(s).append("______ ")
}
val job = new ShellJob
job.shellScript = writeScript(builder.toString)
job.run()
}
private def writeScript(contents: String) = {
val file = BaseTest.createTempFile("temp", "")
IOUtils.writeContents(file, contents)
file
}
}

View File

@ -1,3 +1,3 @@
<ivy-module version="1.0">
<info organisation="edu.mit.broad" module="picard-private-parts" revision="2034" status="integration" publication="20110718185300" />
<info organisation="edu.mit.broad" module="picard-private-parts" revision="2068" status="integration" publication="20111024162900" />
</ivy-module>

View File

@ -1,3 +0,0 @@
<ivy-module version="1.0">
<info organisation="net.sf" module="picard" revision="1.52.944" status="release" />
</ivy-module>

Some files were not shown because too many files have changed in this diff Show More