| ~~ |
| ~~ Licensed to the Apache Software Foundation (ASF) under one or more |
| ~~ contributor license agreements. See the NOTICE file distributed with |
| ~~ this work for additional information regarding copyright ownership. |
| ~~ The ASF licenses this file to You under the Apache License, Version 2.0 |
| ~~ (the "License"); you may not use this file except in compliance with |
| ~~ the License. You may obtain a copy of the License at |
| ~~ |
| ~~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~~ |
| ~~ Unless required by applicable law or agreed to in writing, software |
| ~~ distributed under the License is distributed on an "AS IS" BASIS, |
| ~~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ~~ See the License for the specific language governing permissions and |
| ~~ limitations under the License. |
| ~~ |
| ~~ |
| |
| -------- |
| Apache Commons Exec Tutorial |
| -------- |
| -------- |
| 15 September 2010 |
| -------- |
| |
| Apache Commons Exec |
| |
| * The First Encounter |
| |
| At this point we can safely assume that you would like to start some subprocesses from within your |
| Java application and you spent some time here to do it properly. You look at Commons Exec and think |
| "Wow - calling Runtime.exec() is easy and the Apache folks are wasting their and my time |
| with tons of code". |
| |
| Well, we learned it the hard way (in my case more than once) that using plain Runtime.exec() can be |
| a painful experience. Therefore you are invited to delve into commons-exec and have a look at the |
| hard lessons the easy way ... |
| |
| * Taming Your First Process |
| |
| Let's look at a real example - we would like to print PDF documents from within your Java |
| application. After googling a while it turns out to be a minor headache and using Adobe Acrobat |
| seems to be a good option. |
| |
| The command line under Windows should look like "AcroRd32.exe /p /h file" assuming that the |
| Acrobat Reader is found in the path. |
| |
| +---------------------------------------------------------------------------- |
| String line = "AcroRd32.exe /p /h " + file.getAbsolutePath(); |
| CommandLine cmdLine = CommandLine.parse(line); |
| DefaultExecutor executor = new DefaultExecutor(); |
| int exitValue = executor.execute(cmdLine); |
| +---------------------------------------------------------------------------- |
| |
| You successfully printed your first PDF document but at the end an exception is thrown - what |
| happend? Oops, Acrobat Reader returned an exit value of '1' on success which is usually |
| considered as an execution failure. So we have to tweak our code to fix this odd behavior - |
| we define the exit value of "1" to be considered as successful execution. |
| |
| +---------------------------------------------------------------------------- |
| String line = "AcroRd32.exe /p /h " + file.getAbsolutePath(); |
| CommandLine cmdLine = CommandLine.parse(line); |
| DefaultExecutor executor = new DefaultExecutor(); |
| executor.setExitValue(1); |
| int exitValue = executor.execute(cmdLine); |
| +---------------------------------------------------------------------------- |
| |
| * To Watchdog Or Not To Watchdog |
| |
| You happily printed for a while but now your application blocks - your printing subprocess |
| hangs for some obvious or not so obvious reason. Starting is easy but what to do with a run-away |
| Acrobat Reader telling you that printing failed due to a lack of paper?! Luckily commons-exec |
| provides a watchdog which does the work for you. Here is the improved code which kills a |
| run-away process after sixty seconds. |
| |
| +---------------------------------------------------------------------------- |
| String line = "AcroRd32.exe /p /h " + file.getAbsolutePath(); |
| CommandLine cmdLine = CommandLine.parse(line); |
| DefaultExecutor executor = new DefaultExecutor(); |
| executor.setExitValue(1); |
| ExecuteWatchdog watchdog = new ExecuteWatchdog(60000); |
| executor.setWatchdog(watchdog); |
| int exitValue = executor.execute(cmdLine); |
| +---------------------------------------------------------------------------- |
| |
| * Quoting Is Your Friend |
| |
| Well, the code worked for quite a while until a new customer complained that |
| no documents are printed. It took half a day to find out that the following file |
| 'C:\\Document And Settings\\documents\\432432.pdf' could not be printed. Due to the |
| spaces and without further quoting the command line fell literally apart into |
| the following snippet |
| |
| +---------------------------------------------------------------------------- |
| > AcroRd32.exe /p /h C:\Document And Settings\documents\432432.pdf |
| +---------------------------------------------------------------------------- |
| |
| As a quick fix we added double quotes which tells commons-exec to handle |
| the file as a single command line argument instead of splitting it into |
| parts. |
| |
| +---------------------------------------------------------------------------- |
| String line = "AcroRd32.exe /p /h \"" + file.getAbsolutePath() + "\""; |
| CommandLine cmdLine = CommandLine.parse(line); |
| DefaultExecutor executor = new DefaultExecutor(); |
| executor.setExitValue(1); |
| ExecuteWatchdog watchdog = new ExecuteWatchdog(60000); |
| executor.setWatchdog(watchdog); |
| int exitValue = executor.execute(cmdLine); |
| +---------------------------------------------------------------------------- |
| |
| * Build the Command Line Incrementally |
| |
| The previous problem stems from the fact that commons-exec tried to split |
| a single command line string into a string array considering single and |
| double quotes. At the end of the day this is error-prone so we recommend |
| building the command line incrementally - according to the same reasoning |
| the Ant documentation does not recommend passing a single command line to |
| the <exec> target (see deprecated command attribute for |
| {{{http://ant.apache.org/manual/CoreTasks/exec.html}exec}} task) |
| |
| +---------------------------------------------------------------------------- |
| Map map = new HashMap(); |
| map.put("file", new File("invoice.pdf")); |
| CommandLine cmdLine = new CommandLine("AcroRd32.exe"); |
| cmdLine.addArgument("/p"); |
| cmdLine.addArgument("/h"); |
| cmdLine.addArgument("${file}"); |
| cmdLine.setSubstitutionMap(map); |
| DefaultExecutor executor = new DefaultExecutor(); |
| executor.setExitValue(1); |
| ExecuteWatchdog watchdog = new ExecuteWatchdog(60000); |
| executor.setWatchdog(watchdog); |
| int exitValue = executor.execute(cmdLine); |
| +---------------------------------------------------------------------------- |
| |
| Please note that we are passing an 'java.io.File' instance for expanding |
| the command line arguments - this allows to convert the resulting file name |
| on the fly to match your OS. |
| |
| * Unblock Your Execution |
| |
| Up to now we have a working example but it would not be good enough for |
| production - because it is blocking. |
| |
| Your worker thread will block until the print process has finished or |
| was killed by the watchdog. Therefore executing the print job |
| asynchronously will do the trick. In this example we create an instance |
| of 'ExecuteResultHandler' and pass it to the 'Executor' instance in order |
| to execute the process asynchronously. The 'resultHandler' picks up any |
| offending exception or the process exit code. |
| |
| +---------------------------------------------------------------------------- |
| CommandLine cmdLine = new CommandLine("AcroRd32.exe"); |
| cmdLine.addArgument("/p"); |
| cmdLine.addArgument("/h"); |
| cmdLine.addArgument("${file}"); |
| HashMap map = new HashMap(); |
| map.put("file", new File("invoice.pdf")); |
| cmdLine.setSubstitutionMap(map); |
| |
| DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler(); |
| |
| ExecuteWatchdog watchdog = new ExecuteWatchdog(60*1000); |
| Executor executor = new DefaultExecutor(); |
| executor.setExitValue(1); |
| executor.setWatchdog(watchdog); |
| executor.execute(cmdLine, resultHandler); |
| |
| // some time later the result handler callback was invoked so we |
| // can safely request the exit value |
| int exitValue = resultHandler.waitFor(); |
| +---------------------------------------------------------------------------- |
| |
| * Get Your Hands Dirty |
| |
| A tutorial is nice but executing the tutorial code is even nicer. You find |
| the ready-to-run tutorial under {{{https://commons.apache.org/exec/xref-test/org/apache/commons/exec/TutorialTest.html}src/test/java/org/apache/commons/exec/TutorialTest.java}}. |