2011-03-24 22:03:51 +08:00
/*
2012-01-09 01:11:55 +08:00
* Copyright ( c ) 2012 , The Broad Institute
2011-03-24 22:03:51 +08:00
*
* 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 .
*/
2010-05-24 04:21:09 +08:00
package org.broadinstitute.sting.queue
2010-10-28 03:44:55 +08:00
import function.QFunction
2010-08-10 00:42:48 +08:00
import java.io.File
2010-08-12 05:58:26 +08:00
import org.broadinstitute.sting.commandline._
2010-10-14 23:58:52 +08:00
import org.broadinstitute.sting.queue.util._
2012-10-16 03:03:33 +08:00
import org.broadinstitute.sting.queue.engine. { QStatusMessenger , QGraphSettings , QGraph }
2010-11-13 04:14:28 +08:00
import collection.JavaConversions._
import org.broadinstitute.sting.utils.classloader.PluginManager
2011-02-19 06:21:15 +08:00
import org.broadinstitute.sting.utils.exceptions.UserException
2011-10-25 03:49:02 +08:00
import org.broadinstitute.sting.utils.io.IOUtils
2011-11-24 02:31:39 +08:00
import org.broadinstitute.sting.utils.help.ApplicationDetails
import java.util. { ResourceBundle , Arrays }
import org.broadinstitute.sting.utils.text.TextFormattingUtils
2012-01-09 01:11:55 +08:00
import org.apache.commons.io.FilenameUtils
2010-05-24 04:21:09 +08:00
2011-03-24 22:03:51 +08:00
/* *
* Entry point of Queue . Compiles and runs QScripts passed in to the command line .
*/
2011-09-27 12:50:19 +08:00
object QCommandLine extends Logging {
2011-03-24 22:03:51 +08:00
/* *
* Main .
* @param argv Arguments .
*/
def main ( argv : Array [ String ] ) {
val qCommandLine = new QCommandLine
2011-09-27 12:50:19 +08:00
val shutdownHook = new Thread {
2011-04-27 18:56:39 +08:00
override def run ( ) {
2011-09-27 12:50:19 +08:00
logger . info ( "Shutting down jobs. Please wait..." )
2011-03-24 22:03:51 +08:00
qCommandLine . shutdown ( )
}
2011-09-27 12:50:19 +08:00
}
Runtime . getRuntime . addShutdownHook ( shutdownHook )
2011-03-24 22:03:51 +08:00
try {
2011-10-25 03:49:02 +08:00
CommandLineProgram . start ( qCommandLine , argv )
try {
Runtime . getRuntime . removeShutdownHook ( shutdownHook )
2012-01-09 01:11:55 +08:00
qCommandLine . shutdown ( )
2011-10-25 03:49:02 +08:00
} catch {
2012-08-28 00:04:50 +08:00
case e : Exception => /* ignore, example 'java.lang.IllegalStateException: Shutdown in progress' */
2011-10-25 03:49:02 +08:00
}
2011-03-24 22:03:51 +08:00
if ( CommandLineProgram . result != 0 )
2012-08-28 00:04:50 +08:00
System . exit ( CommandLineProgram . result )
2011-03-24 22:03:51 +08:00
} catch {
case e : Exception => CommandLineProgram . exitSystemWithError ( e )
}
}
}
2010-08-10 00:42:48 +08:00
/* *
* Entry point of Queue . Compiles and runs QScripts passed in to the command line .
*/
class QCommandLine extends CommandLineProgram with Logging {
@Input ( fullName = "script" , shortName = "S" , doc = "QScript scala file" , required = true )
@ClassType ( classOf [ File ] )
2012-01-09 01:11:55 +08:00
var scripts = Seq . empty [ File ]
2010-05-24 04:21:09 +08:00
2010-08-12 05:58:26 +08:00
@ArgumentCollection
2012-01-09 01:11:55 +08:00
val settings = new QGraphSettings
2010-10-21 05:43:52 +08:00
2010-11-13 04:14:28 +08:00
private val qScriptManager = new QScriptManager
private val qGraph = new QGraph
private var qScriptClasses : File = _
2011-10-25 03:49:02 +08:00
private var shuttingDown = false
2010-11-13 04:14:28 +08:00
2012-10-16 03:03:33 +08:00
private lazy val qScriptPluginManager = {
2011-12-17 07:07:26 +08:00
qScriptClasses = IOUtils . tempDir ( "Q-Classes-" , "" , settings . qSettings . tempDirectory )
2010-11-13 04:14:28 +08:00
qScriptManager . loadScripts ( scripts , qScriptClasses )
2012-01-09 01:11:55 +08:00
new PluginManager [ QScript ] ( classOf [ QScript ] , Seq ( qScriptClasses . toURI . toURL ) )
2010-11-13 04:14:28 +08:00
}
2012-10-16 03:03:33 +08:00
private lazy val qStatusMessengerPluginManager = {
new PluginManager [ QStatusMessenger ] ( classOf [ QStatusMessenger ] )
}
2012-10-17 06:49:10 +08:00
ClassFieldCache . parsingEngine = new ParsingEngine ( this )
2010-10-28 03:44:55 +08:00
2010-08-10 00:42:48 +08:00
/* *
* Takes the QScripts passed in , runs their script ( ) methods , retrieves their generated
* functions , and then builds and runs a QGraph based on the dependencies .
*/
def execute = {
2012-10-16 03:03:33 +08:00
val allStatusMessengers = qStatusMessengerPluginManager . createAllTypes ( )
2012-01-09 01:11:55 +08:00
if ( settings . qSettings . runName == null )
settings . qSettings . runName = FilenameUtils . removeExtension ( scripts . head . getName )
2012-08-28 00:04:50 +08:00
if ( IOUtils . isDefaultTempDir ( settings . qSettings . tempDirectory ) )
settings . qSettings . tempDirectory = IOUtils . absolute ( settings . qSettings . runDirectory , ".queue/tmp" )
2012-08-20 22:33:42 +08:00
qGraph . initializeWithSettings ( settings )
2010-08-10 00:42:48 +08:00
2012-10-16 03:03:33 +08:00
for ( statusMessenger <- allStatusMessengers ) {
loadArgumentsIntoObject ( statusMessenger )
}
for ( statusMessenger <- allStatusMessengers ) {
statusMessenger . started ( )
}
val allQScripts = qScriptPluginManager . createAllTypes ( )
2011-08-23 22:09:51 +08:00
for ( script <- allQScripts ) {
2012-10-16 03:03:33 +08:00
logger . info ( "Scripting " + qScriptPluginManager . getName ( script . getClass . asSubclass ( classOf [ QScript ] ) ) )
2010-08-10 00:42:48 +08:00
loadArgumentsIntoObject ( script )
2012-10-17 06:49:10 +08:00
// TODO: Pulling inputs can be time/io expensive! Some scripts are using the files to generate functions-- even for dry runs-- so pull it all down for now.
//if (settings.run)
script . pullInputs ( )
2012-01-09 01:11:55 +08:00
script . qSettings = settings . qSettings
2011-02-19 06:21:15 +08:00
try {
2011-04-27 18:56:39 +08:00
script . script ( )
2011-02-19 06:21:15 +08:00
} catch {
case e : Exception =>
throw new UserException . CannotExecuteQScript ( script . getClass . getSimpleName + ".script() threw the following exception: " + e , e )
}
2010-08-10 00:42:48 +08:00
script . functions . foreach ( qGraph . add ( _ ) )
logger . info ( "Added " + script . functions . size + " functions" )
2010-05-24 04:21:09 +08:00
}
2011-08-23 22:09:51 +08:00
// Execute the job graph
2011-02-16 02:26:14 +08:00
qGraph . run ( )
2010-08-12 05:58:26 +08:00
2012-01-09 01:11:55 +08:00
val functionsAndStatus = qGraph . getFunctionsAndStatus
val success = qGraph . success
2011-08-23 22:09:51 +08:00
// walk over each script, calling onExecutionDone
for ( script <- allQScripts ) {
2012-01-09 01:11:55 +08:00
val scriptFunctions = functionsAndStatus . filterKeys ( f => script . functions . contains ( f ) )
script . onExecutionDone ( scriptFunctions , success )
}
logger . info ( "Script %s with %d total jobs" . format ( if ( success ) "completed successfully" else "failed" , functionsAndStatus . size ) )
2012-08-20 22:33:42 +08:00
// write the final complete job report
logger . info ( "Writing final jobs report..." )
qGraph . writeJobsReport ( )
2011-08-23 22:09:51 +08:00
2012-10-16 03:03:33 +08:00
if ( ! success ) {
2010-10-07 02:29:56 +08:00
logger . info ( "Done with errors" )
2011-02-16 02:26:14 +08:00
qGraph . logFailed ( )
2012-10-16 03:03:33 +08:00
for ( statusMessenger <- allStatusMessengers )
statusMessenger . exit ( "Done with errors" )
2010-10-07 02:29:56 +08:00
1
} else {
2012-10-16 03:03:33 +08:00
if ( settings . run ) {
allQScripts . foreach ( _ . pushOutputs ( ) )
for ( statusMessenger <- allStatusMessengers )
2012-10-17 06:49:10 +08:00
statusMessenger . done ( allQScripts . map ( _ . remoteOutputs ) )
2012-10-16 03:03:33 +08:00
}
2010-10-07 02:29:56 +08:00
0
}
2010-08-10 00:42:48 +08:00
}
/* *
* Returns true as QScripts are located and compiled .
* @return true
*/
override def canAddArgumentsDynamically = true
/* *
2012-10-16 03:03:33 +08:00
* Returns the list of QScripts passed in via - S and other plugins
* so that their arguments can be inspected before QScript . script is called .
* @return Array of dynamic sources
2010-08-10 00:42:48 +08:00
*/
2012-10-16 03:03:33 +08:00
override def getArgumentSources = {
var plugins = Seq . empty [ Class [ _ ] ]
plugins ++= qScriptPluginManager . getPlugins
plugins ++= qStatusMessengerPluginManager . getPlugins
plugins . toArray
}
2010-05-26 06:52:29 +08:00
2010-08-10 00:42:48 +08:00
/* *
2012-10-16 03:03:33 +08:00
* Returns the name of a script / plugin
* @return The name of a script / plugin
2010-08-10 00:42:48 +08:00
*/
2012-10-16 03:03:33 +08:00
override def getArgumentSourceName ( source : Class [ _ ] ) = {
if ( classOf [ QScript ] . isAssignableFrom ( source ) )
qScriptPluginManager . getName ( source . asSubclass ( classOf [ QScript ] ) )
else if ( classOf [ QStatusMessenger ] . isAssignableFrom ( source ) )
qStatusMessengerPluginManager . getName ( source . asSubclass ( classOf [ QStatusMessenger ] ) )
else
null
}
2010-08-10 00:42:48 +08:00
/* *
* Returns a ScalaCompoundArgumentTypeDescriptor that can parse argument sources into scala collections .
* @return a ScalaCompoundArgumentTypeDescriptor
*/
override def getArgumentTypeDescriptors =
Arrays . asList ( new ScalaCompoundArgumentTypeDescriptor )
2011-11-24 02:31:39 +08:00
override def getApplicationDetails : ApplicationDetails = {
new ApplicationDetails ( createQueueHeader ( ) ,
2012-01-09 01:11:55 +08:00
Seq . empty [ String ] ,
2011-11-24 02:31:39 +08:00
ApplicationDetails . createDefaultRunningInstructions ( getClass . asInstanceOf [ Class [ CommandLineProgram ] ] ) ,
"" )
}
2012-01-09 01:11:55 +08:00
private def createQueueHeader ( ) : Seq [ String ] = {
Seq ( String . format ( "Queue v%s, Compiled %s" , getQueueVersion , getBuildTimestamp ) ,
"Copyright (c) 2012 The Broad Institute" ,
2012-10-03 01:34:37 +08:00
"For support and documentation go to http://www.broadinstitute.org/gatk" )
2011-11-24 02:31:39 +08:00
}
private def getQueueVersion : String = {
2012-01-09 01:11:55 +08:00
val stingResources : ResourceBundle = TextFormattingUtils . loadResourceBundle ( "StingText" )
2011-11-24 02:31:39 +08:00
if ( stingResources . containsKey ( "org.broadinstitute.sting.queue.QueueVersion.version" ) ) {
stingResources . getString ( "org.broadinstitute.sting.queue.QueueVersion.version" )
}
else {
"<unknown>"
}
}
private def getBuildTimestamp : String = {
2012-01-09 01:11:55 +08:00
val stingResources : ResourceBundle = TextFormattingUtils . loadResourceBundle ( "StingText" )
2011-11-24 02:31:39 +08:00
if ( stingResources . containsKey ( "build.timestamp" ) ) {
stingResources . getString ( "build.timestamp" )
}
else {
"<unknown>"
}
}
2010-11-13 04:14:28 +08:00
def shutdown ( ) = {
2011-10-25 03:49:02 +08:00
shuttingDown = true
2010-11-13 04:14:28 +08:00
qGraph . shutdown ( )
if ( qScriptClasses != null ) IOUtils . tryDelete ( qScriptClasses )
2010-08-10 00:42:48 +08:00
}
}