//---------------------------------------------------------------------------------- output = "program args".execute().text //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- proc = "vi myfile".execute() proc.waitFor() //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Calling execute() on a String, String[] or List (of Strings or objects with // a toString() method) will fork off another process. // This doesn't replace the existing process but if you simply finish the original // process (leaving the spawned process to finish asynchronously) you will achieve // a similar thing. "archive *.data".execute() ["archive", "accounting.data"].execute() //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // sending text to the input of another process proc = 'groovy -e "print System.in.text.toUpperCase()"'.execute() Thread.start{ def writer = new PrintWriter(new BufferedOutputStream(proc.out)) writer.println('Hello') writer.close() } proc.waitFor() // further process output from process print proc.text.reverse() // => // OLLEH //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // filter your own output keep = System.out pipe = new PipedInputStream() reader = new BufferedReader(new InputStreamReader(pipe)) System.setOut(new PrintStream(new BufferedOutputStream(new PipedOutputStream(pipe)))) int numlines = 2 Thread.start{ while((next = reader.readLine()) != null) { if (numlines-- > 0) keep.println(next) } } (1..8).each{ println it } System.out.close() System.setOut(keep) (9..10).each{ println it } // => // 1 // 2 // 9 // 10 // filtering output by adding quotes and numbers class FilterOutput extends Thread { Closure c Reader reader PrintStream orig FilterOutput(Closure c) { this.c = c orig = System.out def pipe = new PipedInputStream() reader = new BufferedReader(new InputStreamReader(pipe)) System.setOut(new PrintStream(new BufferedOutputStream(new PipedOutputStream(pipe)))) } void run() { def next while((next = reader.readLine()) != null) { c(orig, next) } } def close() { sleep 100 System.out.close() System.setOut(orig) } } cnt = 0 number = { s, n -> cnt++; s.println(cnt + ':' + n) } quote = { s, n -> s.println('> ' + n) } f1 = new FilterOutput(number); f1.start() f2 = new FilterOutput(quote); f2.start() ('a'..'e').each{ println it } f2.close() f1.close() //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Groovy programs (like Java ones) would use streams here. Just process // another stream instead of System.in or System.out: // process url text input = new URL(address).openStream() // ... process 'input' stream // process compressed file input = new GZIPInputStream(new FileInputStream('source.gzip')) // ... process 'input' stream //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // To read STDERR of a process you execute proc = 'groovy -e "println args[0]"'.execute() proc.waitFor() println proc.err.text // => Caught: java.lang.ArrayIndexOutOfBoundsException: 0 ... // To redirect your STDERR to a file System.setErr(new PrintStream(new FileOutputStream("error.txt"))) //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// See 16.2, the technique allows both STDIN and STDOUT of another program to be
// changed at the same time, not just one or the other as per Perl 16.2 solution
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
// See 16.2 and 16.7, the techniques can be combined to allow all three streams
// (STDIN, STDOUT, STDERR) to be altered as required.
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // Groovy can piggy-back on the many options available to Java here: // JIPC provides a wide set of standard primitives: semaphore, event, // FIFO queue, barrier, shared memory, shared and exclusive locks: // http://www.garret.ru/~knizhnik/jipc/jipc.html // sockets allow process to communicate via low-level packets // CORBA, RMI, SOAP allow process to communicate via RPC calls // shared files can also be used // JMS allows process to communicate via a messaging service // Simplist approach is to just link streams: proc1 = 'groovy -e "println args[0]" Hello'.execute() proc2 = 'groovy -e "print System.in.text.toUpperCase()"'.execute() Thread.start{ def reader = new BufferedReader(new InputStreamReader(proc1.in)) def writer = new PrintWriter(new BufferedOutputStream(proc2.out)) while ((next = reader.readLine()) != null) { writer.println(next) } writer.close() } proc2.waitFor() print proc2.text // => HELLO //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// Java/Groovy would normally just use some socket-based technique for communicating
// between processes (see 16.10 for a list of options). If you really must use a named
// pipe, you have these options:
// (1) On *nix machines:
// * Create a named pipe by invoking the mkfifo utility using execute().
// * Open a named pipe by name - which is just like opening a file.
// * Run an external process setting its input and output streams (see 16.1, 16.4, 16.5)
// (2) On Windows machines, Using JCIFS to Connect to Win32 Named Pipes, see:
// http://jcifs.samba.org/src/docs/pipes.html
// Neither of these achieve exactly the same result as the Perl example but some
// scenarios will be almost identical.
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // The comments made in 16.10 regarding other alternative IPC mechanisms also apply here. // This example would normally be done with multiple threads in Java/Groovy as follows. class Shared { String buffer = "not set yet" synchronized void leftShift(value){ buffer = value notifyAll() } synchronized Object read() { return buffer } } shared = new Shared() rand = new Random() threads = [] (1..5).each{ t = new Thread(){ def me = t for (j in 0..9) { shared << "$me.name $j" sleep 100 + rand.nextInt(200) } } t.start() } while(1) { println shared.read() sleep 50 } // => // not set yet // Thread-2 0 // Thread-5 1 // Thread-1 1 // Thread-4 2 // Thread-3 1 // ... // Thread-5 9 // Using JIPC between processes (as a less Groovy alternative that is closer // to the original cookbook) is shown below. // ipcWriterScript: import org.garret.jipc.client.JIPCClientFactory port = 6000 factory = JIPCClientFactory.instance session = factory.create('localhost', port) mutex = session.createMutex("myMutex", false) buffer = session.createSharedMemory("myBuffer", "not yet set") name = args[0] rand = new Random() (0..99).each { mutex.lock() buffer.set("$name $it".toString()) mutex.unlock() sleep 200 + rand.nextInt(500) } session.close() // ipcReaderScript: import org.garret.jipc.client.JIPCClientFactory port = 6000 factory = JIPCClientFactory.instance session = factory.create('localhost', port) mutex = session.createMutex("myMutex", false) buffer = session.createSharedMemory("myBuffer", "not yet set") rand = new Random() (0..299).each { mutex.lock() println buffer.get() mutex.unlock() sleep 150 } session.close() // kick off processes: "java org.garret.jipc.server.JIPCServer 6000".execute() "groovy ipcReaderScript".execute() (0..3).each{ "groovy ipcWriterScript $it".execute() } // => // ... // 0 10 // 2 10 // 2 11 // 1 9 // 1 9 // 1 10 // 2 12 // 3 12 // 3 12 // 2 13 // ... //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Signal handling in Groovy (like Java) is operating system and JVM dependent. // The ISO C standard only requires the signal names SIGABRT, SIGFPE, SIGILL, // SIGINT, SIGSEGV, and SIGTERM to be defined but depending on your platform // other signals may be present, e.g. Windows supports SIGBREAK. For more info // see: http://www-128.ibm.com/developerworks/java/library/i-signalhandling/ // Note: if you start up the JVM with -Xrs the JVM will try to reduce its // internal usage of signals. Also the JVM takes over meany hooks and provides // platform independent alternatives, e.g. see java.lang.Runtime#addShutdownHook() // To see what signals are available for your system (excludes ones taken over // by the JVM): sigs = '''HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM USR1 USR2 CHLD PWR WINCH URG POLL STOP TSTP CONT TTIN TTOU VTALRM PROF XCPU XFSZ WAITING LWP AIO IO INFO THR BREAK FREEZE THAW CANCEL EMT ''' sigs.tokenize(' \n').each{ try { print ' ' + new sun.misc.Signal(it) } catch(IllegalArgumentException iae) {} } // => on Windows XP: // SIGINT SIGILL SIGABRT SIGFPE SIGSEGV SIGTERM SIGBREAK //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // To send a signal to your process: Signal.raise(new Signal("INT")) //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // install a signal handler class DiagSignalHandler implements SignalHandler { ... } diagHandler = new DiagSignalHandler() Signal.handle(new Signal("INT"), diagHandler) //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // temporarily install a signal handler class DiagSignalHandler implements SignalHandler { ... } diagHandler = new DiagSignalHandler() oldHandler = Signal.handle(new Signal("INT"), diagHandler) Signal.handle(new Signal("INT"), oldHandler) //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- import sun.misc.Signal import sun.misc.SignalHandler class DiagSignalHandler implements SignalHandler { private oldHandler // Static method to install the signal handler public static install(signal) { def diagHandler = new DiagSignalHandler() diagHandler.oldHandler = Signal.handle(signal, diagHandler) } public void handle(Signal sig) { println("Diagnostic Signal handler called for signal "+sig) // Output information for each thread def list = [] Thread.activeCount().each{ list += null } Thread[] threadArray = list as Thread[] int numThreads = Thread.enumerate(threadArray) println("Current threads:") for (i in 0..<numThreads) { println(" "+threadArray[i]) } // Chain back to previous handler, if one exists if ( oldHandler != SIG_DFL && oldHandler != SIG_IGN ) { oldHandler.handle(sig) } } } // install using: DiagSignalHandler.install(new Signal("INT")) //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// See 16.17, just don't chain to the previous handler because the default handler
// will abort the process.
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // Groovy relies on Java features here. Java doesn't keep the process around // as it stores metadata in a Process object. You can call waitFor() or destroy() // or exitValue() on the Process object. If the Process object is garbage collected, // the process can still execute asynchronously with respect to the original process. // For ensuring processes don't die, see: // http://jakarta.apache.org/commons/daemon/ //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// There is no equivalent to a signal mask available directly in Groovy or Java.
// You can override and ignore individual signals using recipes 16.16 - 16.18.
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- t = new Timer() t.runAfter(3500){ println 'Took too long' System.exit(1) } def count = 0 6.times{ count++ sleep 1000 println "Count = $count" } t.cancel() // See also special JMX timer class: javax.management.timer.Timer // For an external process you can also use: proc.waitForOrKill(3500) //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // One way to implement this functionality is to automatically replace the ~/.plan // etc. files every fixed timed interval - though this wouldn't be efficient. // Here is a simplified version which is a simplified version compared to the // original cookbook. It only looks at the ~/.signature file and changes it // freely. It also doesn't consider other news reader related files. def sigs = ''' Make is like Pascal: everybody likes it, so they go in and change it. --Dennis Ritchie %% I eschew embedded capital letters in names; to my prose-oriented eyes, they are too awkward to read comfortably. They jangle like bad typography. --Rob Pike %% God made the integers; all else is the work of Man. --Kronecker %% I d rather have :rofix than const. --Dennis Ritchie %% If you want to program in C, program in C. It s a nice language. I use it occasionally... :-) --Larry Wall %% Twisted cleverness is my only skill as a programmer. --Elizabeth Zwicky %% Basically, avoid comments. If your code needs a comment to be understood, it would be better to rewrite it so it s easier to understand. --Rob Pike %% Comments on data are usually much more helpful than on algorithms. --Rob Pike %% Programs that write programs are the happiest programs in the world. --Andrew Hume '''.trim().split(/\n%%\n/) name = 'me@somewhere.org\n' file = new File(System.getProperty('user.home') + File.separator + '.signature') rand = new Random() while(1) { file.delete() file << name + sigs[rand.nextInt(sigs.size())] sleep 10000 } // Another way to implement this functionality (in a completely different way to the // original cookbook) is to use a FileWatcher class, e.g. // http://www.rgagnon.com/javadetails/java-0490.html (FileWatcher and DirWatcher) // http://www.jconfig.org/javadoc/org/jconfig/FileWatcher.html // These file watchers notify us whenever the file is modified, see Pleac chapter 7 // for workarounds to not being able to get last accessed time vs last modified time. // (We would now need to touch the file whenever we accessed it to make it change). // Our handler called from the watchdog class would update the file contents. //---------------------------------------------------------------------------------- |