/src/be/pw/jexif/JExifTool.java

https://bitbucket.org/P_W999/j-exiftool · Java · 576 lines · 361 code · 34 blank · 181 comment · 34 complexity · 0658d0bdce2b85189766a4cdfc02f343 MD5 · raw file

  1. /*******************************************************************************
  2. * Copyright 2012 P_W999
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. ******************************************************************************/
  16. package be.pw.jexif;
  17. import java.io.File;
  18. import java.io.FileNotFoundException;
  19. import java.io.FileWriter;
  20. import java.io.IOException;
  21. import java.nio.charset.Charset;
  22. import java.security.SecureRandom;
  23. import java.text.MessageFormat;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Map.Entry;
  28. import org.apache.commons.exec.CommandLine;
  29. import org.apache.commons.exec.DefaultExecuteResultHandler;
  30. import org.apache.commons.exec.DefaultExecutor;
  31. import org.apache.commons.exec.ExecuteWatchdog;
  32. import org.apache.commons.exec.Executor;
  33. import org.apache.commons.exec.ShutdownHookProcessDestroyer;
  34. import org.slf4j.Logger;
  35. import org.slf4j.LoggerFactory;
  36. import be.pw.jexif.enums.DateTag;
  37. import be.pw.jexif.enums.Errors;
  38. import be.pw.jexif.enums.tag.ExifGPS;
  39. import be.pw.jexif.enums.tag.ExifIFD;
  40. import be.pw.jexif.enums.tag.IFD0;
  41. import be.pw.jexif.enums.tag.Tag;
  42. import be.pw.jexif.exception.ExifError;
  43. import be.pw.jexif.exception.JExifException;
  44. import be.pw.jexif.internal.action.IAction;
  45. import be.pw.jexif.internal.action.impl.ActionFactory;
  46. import be.pw.jexif.internal.constants.ExecutionConstant;
  47. import be.pw.jexif.internal.result.ResultHandler;
  48. import be.pw.jexif.internal.thread.FlushablePumpStreamHandler;
  49. import be.pw.jexif.internal.thread.JExifOutputStream;
  50. import be.pw.jexif.internal.thread.event.DebugHandler;
  51. import be.pw.jexif.internal.thread.event.EventHandler;
  52. import be.pw.jexif.internal.util.Cal10nUtil;
  53. import be.pw.jexif.internal.util.ExiftoolPathUtil;
  54. import be.pw.jexif.internal.util.GPSUtil;
  55. import be.pw.jexif.internal.util.TagUtil;
  56. import com.google.common.annotations.Beta;
  57. import com.google.common.base.Preconditions;
  58. import com.google.common.eventbus.EventBus;
  59. /**
  60. * The JExifTool class acts as the bridge between ExifTool and your java code.<br />
  61. * This class is responsible for starting and stopping the ExifTool process. <br />
  62. * <br />
  63. * In order to use J-ExifTool you must download ExifTool from <a href="http://www.sno.phy.queensu.ca/~phil/exiftool/">http://www.sno.phy.queensu.ca/~phil/exiftool/</a> <br />
  64. * The System Property {@link ExecutionConstant#EXIFTOOLPATH} should point to the executable.<br />
  65. * <br />
  66. * If you want to read or write Exif tags, you first need to make an instance of this class and then use the {@link #getInfo(File)) method to create a {@link JExifInfo} object. <br />
  67. * <b>JExifTool is not thread-safe</b>
  68. * <p>
  69. * The following System Properties are used by J-ExifTool and may or must be set for proper operation:
  70. * <ul>
  71. * <li>{@link ExecutionConstant#EXIFTOOLPATH}: the path to the ExifTool executable</li>
  72. * <li>{@link ExecutionConstant#EXIFTOOLDEADLOCK}: timeout</li>
  73. * <li>{@link ExecutionConstant#EXIFTOOLBYPASSVALIDATION}: whether to bypass validations when writing tags (set to true to bypass, use at own risk)</li>
  74. * </ul>
  75. *
  76. * @author phillip
  77. */
  78. @Beta
  79. public class JExifTool {
  80. /**
  81. * The argsFile is a file on the disk where the command line arguments are stored for ExifTool in -stay_open mode. The path to this file will be passed as the -@ argument.
  82. */
  83. private final File argsFile = new File(System.getProperty("args.path", "args") + (new SecureRandom(Long.toString(System.currentTimeMillis()).getBytes(Charset.forName("UTF-8"))).nextInt()));
  84. /**
  85. * The writer for {@link be.pw.jexif.JExifTool#argsFile}.
  86. */
  87. private FileWriter argsWriter = null;
  88. /**
  89. * The path to the ExifTool executable. If the system property was not defined, the application will back to the current directory and expects the executable to be called "exiftool.exe".
  90. * <p>
  91. * See for more info {@link ExiftoolPathUtil#getPath()}
  92. */
  93. private final File exifToolPath = ExiftoolPathUtil.getPath();
  94. /**
  95. * Timeout in milliseconds before the application decides that the for-looping which is waiting for the ExifTool output has deadlocked.
  96. * <p>
  97. * This might happen if all of a sudden ExifTool crashes or is not up and running.
  98. *
  99. * @see be.pw.jexif.internal.constants.ExecutionConstant#EXIFTOOLDEADLOCK
  100. */
  101. private final int deadLock = Integer.getInteger(ExecutionConstant.EXIFTOOLDEADLOCK, 4000);
  102. /**
  103. * The guava EventBuss will be used to handle the output- and error-streams from the ExifTool process.
  104. */
  105. private final EventBus bus = new EventBus();
  106. /**
  107. * The logger for this class.
  108. */
  109. private static final Logger LOG = LoggerFactory.getLogger(JExifTool.class);
  110. /**
  111. * Apache Commons result handler used for the async process handling.
  112. */
  113. private DefaultExecuteResultHandler resultHandler = null;
  114. /**
  115. * The executor responsible for launching the exif tool binary.
  116. */
  117. private Executor executor = null;
  118. /**
  119. * Default constructor. <br />
  120. * This automatically starts the ExifTool process and registers the standard Tag-set that comes with this library.
  121. *
  122. * @throws JExifException if failed to start ExifTool
  123. */
  124. @Beta
  125. public JExifTool() throws JExifException {
  126. TagUtil.register(IFD0.class);
  127. TagUtil.register(ExifIFD.class);
  128. TagUtil.register(ExifGPS.class);
  129. argsFile.deleteOnExit();
  130. this.start();
  131. }
  132. /**
  133. * Creates a new instance of a JExifInfo object. This object can be used to read and write Exif tags to the specified file.
  134. *
  135. * @param file the image file from which tags shall be read from or written to.
  136. * @return a JExifInfo object.
  137. * @throws IOException if the file does not exist or if it's folder
  138. */
  139. @Beta
  140. public JExifInfo getInfo(final File file) throws IOException {
  141. Preconditions.checkNotNull(file);
  142. if (!file.exists() || file.isDirectory()) {
  143. throw new FileNotFoundException(Cal10nUtil.get(Errors.IO_FILE_NOT_VALID, file.getName()));
  144. }
  145. return new JExifInfo(this, file);
  146. }
  147. /**
  148. * Starts the ExifTool process using Apache Commons Exec This method is automatically called when a new instance of JExifTool is created.
  149. *
  150. * @throws JExifException if failed to start thread.
  151. */
  152. private void start() throws JExifException {
  153. LOG.info("Starting ExifTool");
  154. LOG.trace("Using exifToolPath in: " + exifToolPath.getAbsolutePath());
  155. LOG.trace("Argsfile is stored in: " + argsFile.getAbsolutePath());
  156. if (!exifToolPath.exists() || !exifToolPath.isFile()) {
  157. LOG.error("The provided path to ExifTool is not valid: " + exifToolPath.getAbsolutePath());
  158. LOG.error("To configure the path to your exiftool installation, please set the following system property: " + ExecutionConstant.EXIFTOOLPATH);
  159. LOG.error("or configure the environment variable: " + ExecutionConstant.EXIFTOOLENV);
  160. throw new JExifException(Cal10nUtil.get(Errors.EXIFTOOL_INVALID_PATH));
  161. }
  162. try {
  163. argsWriter = new FileWriter(argsFile);
  164. argsWriter.write("");
  165. argsWriter.flush();
  166. String claFormat;
  167. String cla;
  168. if (System.getProperty("os.name").toLowerCase().contains("windows")) {
  169. claFormat = ExecutionConstant.WINDOWS_CLA;
  170. if (System.getProperty(ExecutionConstant.EXIFTOOLCLIENCODING) == null) {
  171. System.setProperty(ExecutionConstant.EXIFTOOLCLIENCODING, "Cp850");
  172. }
  173. } else {
  174. if (System.getProperty("os.name").toLowerCase().contains("mac")) {
  175. claFormat = ExecutionConstant.MAC_CLA;
  176. if (System.getProperty(ExecutionConstant.EXIFTOOLCLIENCODING) == null) {
  177. System.setProperty(ExecutionConstant.EXIFTOOLCLIENCODING, "UTF-8");
  178. }
  179. } else {
  180. claFormat = ExecutionConstant.LINUX_CLA;
  181. if (System.getProperty(ExecutionConstant.EXIFTOOLCLIENCODING) == null) {
  182. System.setProperty(ExecutionConstant.EXIFTOOLCLIENCODING, "UTF-8");
  183. }
  184. }
  185. }
  186. cla = MessageFormat.format(claFormat, exifToolPath.getCanonicalPath(), argsFile.getCanonicalPath());
  187. LOG.trace("Starting ExifTool with command line arguments {}", cla);
  188. DebugHandler debug = new DebugHandler();
  189. bus.register(debug);
  190. CommandLine cl = CommandLine.parse(cla); // TODO: make better use of CommandLine
  191. resultHandler = new DefaultExecuteResultHandler();
  192. executor = new DefaultExecutor();
  193. executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT));
  194. executor.setStreamHandler(new FlushablePumpStreamHandler(new JExifOutputStream(bus, false), new JExifOutputStream(bus, true)));
  195. executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
  196. executor.execute(cl, resultHandler);
  197. LOG.info("ExifTool was started");
  198. Thread.sleep(250); // wait a bit to ensure process has started
  199. } catch (IOException e) {
  200. LOG.debug("Failed to start ExifTool", e);
  201. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  202. } catch (InterruptedException e) {
  203. LOG.debug("Failed to start ExifTool", e);
  204. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  205. }
  206. }
  207. /**
  208. * Starts the ExifTool process if needed.
  209. *
  210. * @throws JExifException if failed to start ExifTool.
  211. */
  212. private void startIfNecessary() throws JExifException {
  213. if (resultHandler != null) {
  214. if (resultHandler.hasResult() || executor.getWatchdog().killedProcess()) {
  215. LOG.info("Restarting ExifTool");
  216. start();
  217. }
  218. } else {
  219. LOG.trace("ResultHandler was null");
  220. start();
  221. }
  222. }
  223. /**
  224. * Stops the ExifTool-thread. You <b>ALWAYS</b> have to call this method when you don't need J-ExifTool anymore, otherwise the ExifTool process will continue running in the background.
  225. *
  226. * @throws JExifException if it failed to stop (there is a Thread.sleep which may throw an InterruptedException).
  227. */
  228. @Beta
  229. public void stop() throws JExifException {
  230. try {
  231. LOG.info("Stopping ExifTool process");
  232. argsWriter.append(ExecutionConstant.STAY_OPEN).append("\r\n");
  233. argsWriter.append("false\n");
  234. argsWriter.flush();
  235. executor.setWatchdog(new ExecuteWatchdog(1500));
  236. while (!resultHandler.hasResult()) {
  237. // wait for ExifTool to stop
  238. }
  239. LOG.info("ExifTool stopped");
  240. } catch (IOException e) {
  241. LOG.debug("Failed to stop ExifTool", e);
  242. throw new JExifException(Cal10nUtil.get(Errors.IO_CLOSING), e);
  243. }
  244. }
  245. /**
  246. * This method will read out the specified tag from the given file. Default to using the non-exact format.
  247. *
  248. * @param file the file from which to read.
  249. * @param tag the tag to read out.
  250. * @return the tag value as String or null if nothing found.
  251. * @throws JExifException when writing the argsfile went wrong or when the Tread.sleep caused problems.
  252. * @throws ExifError if there was a problem in ExifTool
  253. */
  254. String readTagInfo(final File file, final Tag tag) throws JExifException, ExifError {
  255. return readTagInfo(file, tag, false);
  256. }
  257. /**
  258. * This method will read out the specified tag from the given file. Default to using the non-exact format.
  259. *
  260. * @param file the file from which to read.
  261. * @param tag the tag to read out.
  262. * @param exact whether the exact value should be returned (true) or the human readable format (false)
  263. * @return the tag value as String or null if nothing found.
  264. * @throws JExifException when writing the argsfile went wrong or when the Tread.sleep caused problems (or the execution took to long).
  265. * @throws ExifError if there was a problem in ExifTool
  266. */
  267. String readTagInfo(final File file, final Tag tag, final boolean exact) throws JExifException, ExifError {
  268. LOG.trace("Starting readTagInfo");
  269. IAction action;
  270. EventHandler handler = new EventHandler();
  271. try {
  272. if (exact) {
  273. action = ActionFactory.createExactReadAction(file, tag);
  274. } else {
  275. action = ActionFactory.createReadAction(file, tag);
  276. }
  277. executeAction(handler, action);
  278. } catch (IOException e) {
  279. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  280. } catch (InterruptedException e) {
  281. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  282. }
  283. List<String> results = handler.getResultList();
  284. List<String> errors = handler.getErrorList();
  285. ResultHandler.run(action, results, errors);
  286. return action.getResult().get(tag);
  287. }
  288. /**
  289. * This method will write a value to a given Tag.
  290. *
  291. * @param file the file to which the value should be written.
  292. * @param tag the tag that should be written.
  293. * @param value the value to write (if null or empty string, the Tag will be cleared)
  294. * @throws JExifException when writing the argsfile went wrong or when the Tread.sleep caused problems (or the execution took to long).
  295. * @throws ExifError if there was a problem in ExifTool
  296. */
  297. void writeTagInfo(final File file, final Tag tag, final String value) throws ExifError, JExifException {
  298. LOG.trace("Starting readTagInfo");
  299. IAction action;
  300. EventHandler handler = new EventHandler();
  301. try {
  302. Map<Tag, String> valuesToWrite = new HashMap<>(1);
  303. valuesToWrite.put(tag, value);
  304. action = ActionFactory.createTagWriteAction(file, valuesToWrite);
  305. executeAction(handler, action);
  306. } catch (IOException e) {
  307. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  308. } catch (InterruptedException e) {
  309. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  310. }
  311. List<String> results = handler.getResultList();
  312. List<String> errors = handler.getErrorList();
  313. ResultHandler.run(action, results, errors);
  314. }
  315. /**
  316. * This method will write a value to a given Tag.
  317. *
  318. * @param file the file to which the value should be written.
  319. * @param valuesToWrite the value to write
  320. * @throws JExifException when writing the argsfile went wrong or when the Tread.sleep caused problems (or the execution took to long).
  321. * @throws ExifError if there was a problem in ExifTool
  322. */
  323. void writeGPSTagInfo(final File file, final Map<Tag, String> valuesToWrite) throws ExifError, JExifException {
  324. LOG.trace("Starting readTagInfo");
  325. IAction action;
  326. EventHandler handler = new EventHandler();
  327. try {
  328. GPSUtil.validateGPSValues(valuesToWrite);
  329. GPSUtil.formatGPSValues(valuesToWrite);
  330. action = ActionFactory.createTagWriteAction(file, valuesToWrite);
  331. handler = new EventHandler();
  332. executeAction(handler, action);
  333. } catch (IOException e) {
  334. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  335. } catch (InterruptedException e) {
  336. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  337. }
  338. List<String> results = handler.getResultList();
  339. List<String> errors = handler.getErrorList();
  340. ResultHandler.run(action, results, errors);
  341. }
  342. /**
  343. * Returns all the tags, even the ones not known in J-ExifTool.
  344. *
  345. * @param file the file from which to read.
  346. * @param exact whether the exact value should be returned or not.
  347. * @return the map with tag-name - tag-value.
  348. * @throws JExifException when writing the argsfile went wrong or when the Tread.sleep caused problems (or the execution took to long).
  349. * @throws ExifError if there was a problem in ExifTool
  350. */
  351. @SuppressWarnings("unchecked")
  352. Map<String, String> getAllTagInfo(final File file, final boolean exact) throws ExifError, JExifException {
  353. return (Map<String, String>) getAllTagInfo(file, exact, false);
  354. }
  355. /**
  356. * Returns all the tags known to J-ExifTool.
  357. *
  358. * @param file the file from which to read.
  359. * @param exact whether the exact value should be returned or not.
  360. * @return the map with tag-name - tag-value.
  361. * @throws JExifException when writing the argsfile went wrong or when the Tread.sleep caused problems (or the execution took to long).
  362. * @throws ExifError if there was a problem in ExifTool
  363. */
  364. @SuppressWarnings("unchecked")
  365. Map<Tag, String> getAllSupportedTagInfo(final File file, final boolean exact) throws ExifError, JExifException {
  366. return (Map<Tag, String>) getAllTagInfo(file, exact, true);
  367. }
  368. void timeShift(final File file, final String shift, final DateTag tag) throws JExifException, ExifError {
  369. LOG.trace("Starting timeShift");
  370. IAction action;
  371. EventHandler handler = new EventHandler();
  372. try {
  373. action = ActionFactory.createDateTimeShiftAction(file, shift, tag);
  374. executeAction(handler, action);
  375. } catch (IOException e) {
  376. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  377. } catch (InterruptedException e) {
  378. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  379. }
  380. List<String> results = handler.getResultList();
  381. List<String> errors = handler.getErrorList();
  382. ResultHandler.run(action, results, errors);
  383. }
  384. void copyFrom(final File to, final File from, final Tag... tags) throws JExifException, ExifError {
  385. LOG.trace("Starting copyFrom");
  386. IAction action;
  387. EventHandler handler = new EventHandler();
  388. try {
  389. action = ActionFactory.createCopyFromAction(from, to, tags);
  390. executeAction(handler, action);
  391. } catch (IOException e) {
  392. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  393. } catch (InterruptedException e) {
  394. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  395. }
  396. List<String> results = handler.getResultList();
  397. List<String> errors = handler.getErrorList();
  398. ResultHandler.run(action, results, errors);
  399. }
  400. /**
  401. * Extracts a thumnail image to a file.
  402. *
  403. * @param file the file
  404. * @param format the file name format for the thumnbail image (see {@link be.pw.jexif.internal.action.IThumbnailAction}
  405. * @throws JExifException
  406. * @throws ExifError
  407. */
  408. void extractThumbnail(final File file, final String format) throws JExifException, ExifError {
  409. LOG.trace("Starting extractThumbnail");
  410. IAction action;
  411. EventHandler handler = new EventHandler();
  412. try {
  413. action = ActionFactory.createThumnailAction(file, format);
  414. executeAction(handler, action);
  415. } catch (IOException e) {
  416. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  417. } catch (InterruptedException e) {
  418. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  419. }
  420. List<String> results = handler.getResultList();
  421. List<String> errors = handler.getErrorList();
  422. ResultHandler.run(action, results, errors);
  423. }
  424. /**
  425. * Delets all known exiftags from a jpeg file.
  426. *
  427. * @param file the file to clear
  428. * @throws JExifException if something goest wrong internally
  429. * @throws ExifError if something goes wrong in exiftool
  430. */
  431. void deleteAllExifTags(final File file) throws JExifException, ExifError {
  432. LOG.trace("Starting delete all exif tags");
  433. IAction action;
  434. EventHandler handler = new EventHandler();
  435. try {
  436. action = ActionFactory.createDeleteAllAction(file);
  437. executeAction(handler, action);
  438. } catch (IOException e) {
  439. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  440. } catch (InterruptedException e) {
  441. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  442. }
  443. List<String> results = handler.getResultList();
  444. List<String> errors = handler.getErrorList();
  445. ResultHandler.run(action, results, errors);
  446. }
  447. /**
  448. * Returns tags from a file.
  449. *
  450. * @param file the file from which to read the tags
  451. * @param exact whether the exact (true) or human readable format (false) should be used
  452. * @param onlySupported if only supported tags should be extracted (returns Tag objects as key)
  453. * @return a map with as key a String (onlySupported=false) or a Tag (onlySupported=true)
  454. * @throws ExifError if something goes wrong in exiftool
  455. * @throws JExifException if something goes wrong internally
  456. */
  457. private Map<? extends Object, String> getAllTagInfo(final File file, final boolean exact, final boolean onlySupported) throws ExifError, JExifException {
  458. LOG.trace("Starting readTagInfo");
  459. IAction action;
  460. EventHandler handler = new EventHandler();
  461. try {
  462. if (exact) {
  463. action = ActionFactory.createTagReadExactAllAction(file);
  464. } else {
  465. action = ActionFactory.createTagReadAllAction(file);
  466. }
  467. executeAction(handler, action);
  468. } catch (IOException e) {
  469. throw new JExifException(Cal10nUtil.get(Errors.IO_ARGSFILE), e);
  470. } catch (InterruptedException e) {
  471. throw new JExifException(Cal10nUtil.get(Errors.INTERRUPTED_SLEEP), e);
  472. }
  473. List<String> results = handler.getResultList();
  474. List<String> errors = handler.getErrorList();
  475. ResultHandler.run(action, results, errors);
  476. if (onlySupported) {
  477. return action.getResult();
  478. } else {
  479. Map<String, String> tags = action.getUnsupportedTags();
  480. for (Entry<Tag, String> e : action.getResult().entrySet()) {
  481. tags.put(e.getKey().getName(), e.getValue());
  482. }
  483. return tags;
  484. }
  485. }
  486. /**
  487. * This method will write the exiftool execution arguments to the argument file and wait until the handler has finished aquiring the results.
  488. *
  489. * @param handler the handler which will be registered to the bus in order to process the exiftool output
  490. * @param action the action to execute
  491. * @throws InterruptedException in case the thread can not be suspended while waiting for output
  492. * @throws JExifException if something goes wrong internally
  493. * @throws IOException if there was a problem writing the arguments to the args file
  494. */
  495. private void executeAction(final EventHandler handler, final IAction action) throws InterruptedException, JExifException, IOException {
  496. try {
  497. startIfNecessary();
  498. int i = 0;
  499. bus.register(handler);
  500. try {
  501. File exiftoolTemp = new File(action.getFile().getCanonicalPath() + "_exiftool_tmp");
  502. if (exiftoolTemp.exists()) {
  503. LOG.debug("Exiftool temporary file found. Deleting it");
  504. exiftoolTemp.delete();
  505. }
  506. } catch (Exception e) {
  507. LOG.warn("An error occured trying to delete exiftool temp file", e);
  508. LOG.warn("J-Exiftool will continue it's attempt to execute the action");
  509. }
  510. String[] arguments = action.buildArguments();
  511. argsWriter.append(ExecutionConstant.ECHO).append("\r\n");
  512. argsWriter.append(ExecutionConstant.START).append(" ").append(action.getId()).append("\r\n");
  513. for (String argument : arguments) {
  514. argsWriter.append(argument).append("\r\n");
  515. }
  516. argsWriter.append(ExecutionConstant.EXECUTE).append("\r\n");
  517. argsWriter.append(ExecutionConstant.ECHO).append("\r\n");
  518. argsWriter.append(ExecutionConstant.STOP).append(" ").append(action.getId()).append("\r\n");
  519. argsWriter.append(ExecutionConstant.EXECUTE).append("\r\n");
  520. argsWriter.append(ExecutionConstant.ECHO).append("\r\n");
  521. argsWriter.append(ExecutionConstant.FLUSH).append("\r\n");
  522. argsWriter.append(ExecutionConstant.EXECUTE).append("\r\n");
  523. argsWriter.flush();
  524. while (!handler.isFinished() && i <= deadLock) {
  525. Thread.sleep(50);
  526. i += 50;
  527. }
  528. if (!handler.isFinished()) {
  529. if (executor instanceof FlushablePumpStreamHandler) {
  530. ((FlushablePumpStreamHandler) executor.getStreamHandler()).flush();
  531. }
  532. Thread.sleep(100);
  533. }
  534. if (!handler.isFinished()) {
  535. LOG.error(Cal10nUtil.get(Errors.DEADLOCK, i));
  536. throw new JExifException(Cal10nUtil.get(Errors.DEADLOCK, i));
  537. }
  538. } finally {
  539. bus.unregister(handler);
  540. }
  541. }
  542. }