All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.thebuzzmedia.exiftool.ExifToolBuilder Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/**
 * Copyright 2011 The Buzz Media, LLC
 * Copyright 2015 Mickael Jeanroy 
 *
 * Licensed 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.
 */

package com.thebuzzmedia.exiftool;

import com.thebuzzmedia.exiftool.core.schedulers.DefaultScheduler;
import com.thebuzzmedia.exiftool.core.schedulers.NoOpScheduler;
import com.thebuzzmedia.exiftool.core.strategies.DefaultStrategy;
import com.thebuzzmedia.exiftool.core.strategies.PoolStrategy;
import com.thebuzzmedia.exiftool.core.strategies.StayOpenStrategy;
import com.thebuzzmedia.exiftool.logs.Logger;
import com.thebuzzmedia.exiftool.logs.LoggerFactory;
import com.thebuzzmedia.exiftool.process.CommandExecutor;
import com.thebuzzmedia.exiftool.process.executor.CommandExecutors;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static com.thebuzzmedia.exiftool.core.schedulers.SchedulerDuration.millis;
import static com.thebuzzmedia.exiftool.process.executor.CommandExecutors.newExecutor;

/**
 * Builder for {@link ExifTool} instance.
 * This builder should be used to create instance of {@link com.thebuzzmedia.exiftool.ExifTool}.
 *
 * 

Settings

* *

Path

* * Set the absolute withPath to the ExifTool executable on the host system running * this class as defined by the `exiftool.withPath` system property. * * If set, value will be used, otherwise, withPath will be read: *
    *
  • * From system property. This system property can be set on startup * with {@code -Dexiftool.withPath=/withPath/to/exiftool} or by * calling {@link System#setProperty(String, String)} before * this class is loaded. *
  • *
  • * Default value is {@code exiftool}. In this case, {@code exiftool} * command must be globally available. *
  • *
* * If ExifTool is on your system withPath and running the command {@code exiftool} * successfully executes it, leaving this value unchanged will work fine on * any platform. If the ExifTool executable is named something else or not * in the system withPath, then this property will need to be set to point at it * before using this class. * * On Windows be sure to double-escape the withPath to the tool, * for example: {@code -Dexiftool.withPath=C:\\Tools\\exiftool.exe}. * * Default value is {@code exiftool}. * * Relative withPath values (e.g. {@code bin/tools/exiftool}) are executed with * relation to the base directory the VM process was started in. Essentially * the directory that {@code new File(".").getAbsolutePath()} points at * during runtime. * *

Executor

* * Executor is the component responsible for executing command line on the * system. Most of the time, the default should be fine, but if you want to tune * the used withExecutor, then this property is for you. * Custom withExecutor must implement {@link com.thebuzzmedia.exiftool.process.CommandExecutor} interface. * *

Stay Open Strategy

* * ExifTool 8.36 * added a new persistent-process feature that allows ExifTool to stay * running in a daemon mode and continue accepting commands via a file or stdin. * This feature is disabled by default. * * NOTE: If {@code stay_open} flag is enabled, then an * instance of {@link com.thebuzzmedia.exiftool.exceptions.UnsupportedFeatureException} * may be thrown during ExifTool creation. * * If this exception occurs, then you should probably: *
    *
  • Update your ExifTool version.
  • *
  • Create new ExifTool without this feature.
  • *
. * * Usage: * *

 *     final ExifTool exifTool;
 *     try {
 *         exifTool = new ExifToolBuilder()
 *             .enableStayOpen()
 *             .build();
 *     }
 *     catch (UnsupportedFeatureException ex) {
 *         exifTool = new ExifToolBuilder().build();
 *     }
 * 
* *

Custom Strategies

* * If default strategies are not enough, you can easily provide your own using * the {@link #withStrategy} method. * * Usage: * *

 *   ExifTool exifTool = new ExifToolBuilder()
 *     .withStrategy(new MyCustomStrategy())
 *     .build();
 * 
*/ public class ExifToolBuilder { /** * Class Logger. */ private static final Logger log = LoggerFactory.getLogger(ExifToolBuilder.class); /** * Function to get default path value. */ private static final PathFunction PATH = new PathFunction(); /** * Function to get default cleanup interval. */ private static final DelayFunction DELAY = new DelayFunction(); /** * Function to get default executor environment. */ private static final ExecutorFunction EXECUTOR = new ExecutorFunction(); /** * ExifTool path. */ private String path; /** * ExifTool executor. */ private CommandExecutor executor; /** * Check if {@code stay_open} flag should be enabled. */ private Boolean stayOpen; /** * Cleanup Delay. */ private Long cleanupDelay; /** * Custom execution strategy. */ private ExecutionStrategy strategy; /** * Custom Scheduler. */ private Scheduler scheduler; /** * Pool size. */ private int poolSize; public ExifToolBuilder() { } /** * Override default path. * Default path is defined by the environment property {@code exiftool.path} or is * set with {@code exiftool} otherwise. Setting the path explicitly will disable automatic * lookup. * * @param path New path. * @return Current builder. */ public ExifToolBuilder withPath(String path) { log.debug("Set path: {}", path); this.path = path; return this; } /** * Override default path. * Default path is defined by the environment property {@code exiftool.path} or is * set with {@code exiftool} otherwise. Setting the path explicitly will disable automatic * lookup. * *
* * Note: If path is not an executable file, a warning * will be logged but it will not fail. * * @param path New path. * @return Current builder. */ public ExifToolBuilder withPath(File path) { log.debug("Set path: {}", path); if (!path.canExecute()) { log.warn("Executable {} is not executable, exiftool may fail later", path); } this.path = path.getAbsolutePath(); return this; } /** * Override default exifTool executor. * * @param executor New withExecutor. * @return Current builder. */ public ExifToolBuilder withExecutor(CommandExecutor executor) { log.debug("Set withExecutor: {}", executor); this.executor = executor; return this; } /** * Enable {@code stay_open} feature. * * @return Current builder. */ public ExifToolBuilder enableStayOpen() { log.debug("Enable 'stay_open' feature"); // If strategy has already been defined, log a warning. if (strategy != null) { log.warn("A custom strategy is defined, enabling 'stay_open' feature will be ignored"); } this.stayOpen = true; return this; } /** * Enable {@code stay_open} feature. * * Note: * *
    *
  • * If {link #withStrategy} is called, then calling this method * is useless. *
  • *
  • * If {link #enableStayOpen(scheduler} is called, then calling this method is * useless. *
  • *
* * @param cleanupDelay Interval (in milliseconds) between automatic clean operation. * @return Current builder. */ public ExifToolBuilder enableStayOpen(long cleanupDelay) { log.debug("Enable 'stay_open' feature"); if (strategy != null) { log.warn("A custom strategy is defined, enabling 'stay_open' feature will be ignored"); } if (scheduler != null) { log.warn("A custom scheduler is already defined, it will be ignored"); } this.stayOpen = true; this.cleanupDelay = cleanupDelay; return this; } /** * Enable {@code stay_open} feature and perform cleanup task using given {@code scheduler}. * * Note: * *
    *
  • If {@link #withStrategy} has already been called, then calling is useless.
  • *
  • * If {@link #enableStayOpen(long)} has already been called, then given {@code delay} will be * ignored and the specified scheduler will be used. *
  • *
* * @param scheduler Scheduler used to process automatic cleanup task.. * @return Current builder. */ public ExifToolBuilder enableStayOpen(Scheduler scheduler) { log.debug("Enable 'stay_open' feature"); if (strategy != null) { log.warn("A custom strategy is defined, enabling 'stay_open' feature will be ignored"); } if (cleanupDelay != null) { log.warn("Custom scheduler is defined, previous delay will be ignored"); } this.stayOpen = true; this.scheduler = scheduler; return this; } /** * Override default execution strategy. * * If {@link #enableStayOpen} has been called, then strategy associated with {@code stay_open} flag * will be ignored. * * @param strategy Strategy. * @return Current builder. */ public ExifToolBuilder withStrategy(ExecutionStrategy strategy) { log.debug("Overriding default strategy"); if (stayOpen != null) { log.warn("Flag 'stay_open' has been enabled and you are overriding the default execution strategy."); log.warn("Enabling 'stay_open' feature will be ignored"); } this.strategy = strategy; return this; } /** * Override default execution strategy: * *
    *
  • a pool of {@link StayOpenStrategy} with a size of {@code poolSize} will be used.
  • *
  • Default scheduler instances will be used with a delay of {@code cleanupDelay}.
  • *
* * @param poolSize Pool size. * @param cleanupDelay Cleanup delay for each scheduler of pool elements. * @return Current builder. */ public ExifToolBuilder withPoolSize(int poolSize, long cleanupDelay) { log.debug("Overriding default strategy"); if (poolSize > 0) { this.poolSize = poolSize; this.cleanupDelay = cleanupDelay; } else { log.warn("Pool size has been enabled with a value less or equal than zero, ignore it."); } return this; } /** * Override default execution strategy: * *
    *
  • a pool of {@link StayOpenStrategy} with a size of {@code poolSize} will be used.
  • *
  • No cleanup scheduler will be used (use {@link #withPoolSize(int, long)} instead.
  • *
* * @param poolSize Pool size. * @return Current builder. */ public ExifToolBuilder withPoolSize(int poolSize) { log.debug("Overriding default strategy"); if (poolSize > 0) { this.poolSize = poolSize; this.cleanupDelay = 0L; } else { log.warn("Pool size has been enabled with a value less or equal than zero, ignore it."); } return this; } /** * Create exiftool instance with previous settings. * * @return Exiftool instance. */ public ExifTool build() { String path = firstNonNull(this.path, PATH); CommandExecutor executor = firstNonNull(this.executor, EXECUTOR); ExecutionStrategy strategy = firstNonNull(this.strategy, new StrategyFunction(stayOpen, cleanupDelay, scheduler, poolSize)); // Add some debugging information if (log.isDebugEnabled()) { log.debug("Create ExifTool instance:"); log.debug(" - Path: {}", path); log.debug(" - Executor: {}", executor); log.debug(" - Strategy: {}", strategy); log.debug(" - StayOpen: {}", stayOpen); } return new ExifTool(path, executor, strategy); } /** * Return first non null value: *
    *
  • If first parameter is not null, then it is returned.
  • *
  • Otherwise, result of function is returned.
  • *
* * @param value First value. * @param factory Function used to get non null value. * @param Type of values. * @return Non null value. */ private static T firstNonNull(T value, FactoryFunction factory) { return value == null ? factory.apply() : value; } /** * Interface to return values. * This interface should be used by builder to lazily create * default settings parameters. * * @param Type of settings. */ private static interface FactoryFunction { T apply(); } /** * Return the absolute path to the ExifTool executable on the host system running * this class as defined by the {@code exiftool.path} system property. * * This system property can be set on startup with {@code -Dexiftool.path=/path/to/exiftool} * or by calling {@link System#setProperty(String, String)} before * this class is loaded. * * On Windows be sure to double-escape the path to the tool, * for example: {@code -Dexiftool.path=C:\\Tools\\exiftool.exe}. * * Default value is {@code exiftool}. */ private static class PathFunction implements FactoryFunction { @Override public String apply() { return System.getProperty("exiftool.path", "exiftool"); } } /** * Return the interval (in milliseconds) of inactivity before the cleanup thread wakes * up and cleans up the daemon ExifTool process and the read/write streams * used to communicate with it when the {@code stay_open} feature is * used. * * Ever time a call to {@link ExifTool#getImageMeta} is processed, the timer * keeping track of cleanup is reset; more specifically, this class has to * experience no activity for this duration of time before the cleanup * process is fired up and cleans up the host OS process and the stream * resources. * * Any subsequent calls to {@link ExifTool#getImageMeta} after a cleanup simply * re-initializes the resources. * * This system property can be set on startup with {@code -Dexiftool.processCleanupDelay=600000} * or by calling {@link System#setProperty(String, String)} before * this class is loaded. * * Setting this value to 0 disables the automatic cleanup thread completely * and the caller will need to manually cleanup the external ExifTool * process and read/write streams by calling {@link ExifTool#close} method. * * Default value is {@code 600, 000} (10 minutes). */ private static class DelayFunction implements FactoryFunction { @Override public Long apply() { return Long.getLong("exiftool.processCleanupDelay", 600000); } } /** * Returns the default executor for the created exifTool instance. * Default executor is the result of {@link CommandExecutors#newExecutor()} method. */ private static class ExecutorFunction implements FactoryFunction { @Override public CommandExecutor apply() { return newExecutor(); } } /** * Create scheduler used to perform automatic cleanup task. * Default scheduler will depend on the given {@code delay}: *
    *
  • If {@code delay} is less than or equal to zero, then an instance of {@link NoOpScheduler} will be returned.
  • *
  • If {@code delay} is greater than zero, then an instance of {@link DefaultScheduler} will be returned.
  • *
*/ private static class SchedulerFunction implements FactoryFunction { private final Long delay; public SchedulerFunction(Long delay) { this.delay = delay; } @Override public Scheduler apply() { // Otherwise, this is the StayOpen strategy. // We have to look up the delay between automatic clean and create // the scheduler. final long delay = firstNonNull(this.delay, DELAY); return delay > 0 ? new DefaultScheduler(millis(delay)) : new NoOpScheduler(); } } /** * Returns the {@link ExecutionStrategy} to use with {@link ExifTool} instances. * *

Default

* By default, a really simple strategy is used (instance of {@link DefaultStrategy}). * *

StayOpen

* If the {@code stay_open} has been enabled, then an instance of {@link StayOpenStrategy} * will be created. For this strategy, a scheduler will be created. This scheduler will be used to run * a task to clean resources used by this strategy. This task will run automatically after a specified * delay. */ private static class StrategyFunction implements FactoryFunction { private final Boolean stayOpen; private final Long delay; private final Scheduler scheduler; private int poolSize; public StrategyFunction(Boolean stayOpen, Long delay, Scheduler scheduler, int poolSize) { this.stayOpen = stayOpen; this.delay = delay; this.scheduler = scheduler; this.poolSize = poolSize; } @Override public ExecutionStrategy apply() { // First, try the pool strategy. if (poolSize > 0) { List strategies = new ArrayList<>(poolSize); for (int i = 0; i < poolSize; i++) { Scheduler scheduler = new SchedulerFunction(delay).apply(); StayOpenStrategy strategy = new StayOpenStrategy(scheduler); strategies.add(strategy); } return new PoolStrategy(strategies); } // Try the stayOpen strategy. if (stayOpen != null && stayOpen) { return new StayOpenStrategy(firstNonNull(scheduler, new SchedulerFunction(delay))); } // Simple use case: nothing has been parametrized, so // just return the default strategy. return new DefaultStrategy(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy