Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/com/thebuzzmedia/exiftool/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ public interface ExecutionStrategy extends AutoCloseable {
*/
void execute(CommandExecutor executor, String exifTool, List<String> arguments, OutputHandler handler) throws IOException;

/**
* Set the ExifTool config file path.
* The default is {@code .ExifTool_config}.
* <p>
* Use {@code null} to specify no custom config path,
* and an empty string to disable loading the default config file.
*
* @param configPath Path to the new config path, or {@code null} for no config path.
* @implNote The default implementation does nothing, and only exists for backwards compatibility.
* Implementers should always override it.
* @see <a href="https://exiftool.org/config.html">exiftool.org/config.html</a>
*/
default void setConfigFilePath(String configPath) {
}

/**
* Check if exiftool process is currently running.
* This method is important especially if {@code stay_open} flag has been enabled.
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/com/thebuzzmedia/exiftool/ExifToolBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ public class ExifToolBuilder {
*/
private String path;

/**
* ExifTool config file path.
*/
private String configPath;

/**
* ExifTool executor.
*/
Expand Down Expand Up @@ -229,6 +234,39 @@ public ExifToolBuilder withPath(File path) {
return this;
}

/**
* Override the default ExifTool config path.
* The default path is {@code .ExifTool_config}.
* <p>
* Set this to an empty string to disable loading the default config file.
*
* @param configPath New config path.
* @return Current builder.
* @see <a href="https://exiftool.org/config.html">exiftool.org/config.html</a>
*/
public ExifToolBuilder withConfig(String configPath) {
log.debug("Set configPath: {}", configPath);
this.configPath = configPath;
return this;
}

/**
* Override the default ExifTool config path.
* The default path is {@code .ExifTool_config}.
* <p>
* Call {@link #withConfig(String)} with an empty string to
* disable loading the default config file.
*
* @param configPath New config path.
* @return Current builder.
* @see <a href="https://exiftool.org/config.html">exiftool.org/config.html</a>
*/
public ExifToolBuilder withConfig(File configPath) {
log.debug("Set configPath: {}", configPath);
this.configPath = configPath.getAbsolutePath();
return this;
}

/**
* Override default exifTool executor.
*
Expand Down Expand Up @@ -405,6 +443,7 @@ 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));
strategy.setConfigFilePath(configPath);

// Add some debugging information
if (log.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ public class DefaultStrategy implements ExecutionStrategy {
*/
private static final Logger log = LoggerFactory.getLogger(DefaultStrategy.class);

/**
* Path to the ExifTool config file,
* or {@code null} if no custom config file is specified.
*/
private String configPath;

/**
* Create strategy.
*/
Expand All @@ -59,14 +65,23 @@ public DefaultStrategy() {
public void execute(CommandExecutor executor, String exifTool, List<String> arguments, OutputHandler handler) throws IOException {
log.debug("Using ExifTool in non-daemon mode (-stay_open False)...");

Command cmd = CommandBuilder.builder(exifTool, arguments.size() + 2)
CommandBuilder cmdBuilder = CommandBuilder.builder(exifTool, arguments.size() + (configPath == null ? 2 : 4));
if (configPath != null) {
cmdBuilder.addArgument("-config", configPath);
}
Command cmd = cmdBuilder
.addArgument("-sep", Constants.SEPARATOR)
.addAll(arguments)
.build();

executor.execute(cmd, handler);
}

@Override
public void setConfigFilePath(String configPath) {
this.configPath = configPath;
}

@Override
public boolean isSupported(Version version) {
// Always true.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ public void execute(CommandExecutor executor, String exifTool, List<String> argu
}
}

@Override
public void setConfigFilePath(String configPath) {
for (ExecutionStrategy executionStrategy : pool) {
executionStrategy.setConfigFilePath(configPath);
}
}

@Override
public boolean isRunning() {
return pool.size() < poolSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ public class StayOpenStrategy implements ExecutionStrategy {
*/
private CommandProcess process;

/**
* Path to the ExifTool config file,
* or {@code null} if no custom config file is specified.
*/
private String configPath;

/**
* Create strategy.
* Scheduler provided in parameter will be used to clean resources (exiftool process).
Expand All @@ -82,7 +88,11 @@ public void execute(CommandExecutor executor, String exifTool, List<String> argu
// ready to receive commands from us.
if (process == null || process.isClosed()) {
log.debug("Start exiftool process");
process = executor.start(CommandBuilder.builder(exifTool, 6)
CommandBuilder cmdBuilder = CommandBuilder.builder(exifTool, (configPath == null ? 6 : 8));
if (configPath != null) {
cmdBuilder.addArgument("-config", configPath);
}
process = executor.start(cmdBuilder
.addArgument("-stay_open", "True")
.addArgument("-sep", Constants.SEPARATOR)
.addArgument("-@")
Expand All @@ -106,6 +116,11 @@ public void execute(CommandExecutor executor, String exifTool, List<String> argu
}
}

@Override
public void setConfigFilePath(String configPath) {
this.configPath = configPath;
}

@Override
public synchronized boolean isRunning() {
return process != null && process.isRunning();
Expand Down
19 changes: 19 additions & 0 deletions src/test/java/com/thebuzzmedia/exiftool/ExifToolBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class ExifToolBuilderTest {
private ExecutionStrategy strategy;
private Scheduler scheduler;
private String path;
private String configPath;

private ExifToolBuilder builder;

Expand All @@ -68,6 +69,7 @@ void setUp() throws Exception {

builder = new ExifToolBuilder();
path = "/foo";
configPath = "/bar";

// Mock ExifTool Version
CommandResult v9_36 = new CommandResultBuilder()
Expand All @@ -93,6 +95,21 @@ void it_should_update_path_with_file() {
assertThat(builder).extracting("path").isEqualTo(file.getAbsolutePath());
}

@Test
void it_should_update_config_path() {
ExifToolBuilder res = builder.withConfig(configPath);
assertThat(res).isSameAs(builder);
assertThat(builder).extracting("configPath").isEqualTo(configPath);
}

@Test
void it_should_update_config_path_with_file() {
File file = new FileBuilder("config.config").build();
ExifToolBuilder res = builder.withConfig(file);
assertThat(res).isSameAs(builder);
assertThat(builder).extracting("configPath").isEqualTo(file.getAbsolutePath());
}

@Test
void it_should_update_executor() {
ExifToolBuilder res = builder.withExecutor(executor);
Expand Down Expand Up @@ -149,13 +166,15 @@ void it_should_override_strategy() {
void it_should_create_exiftool_with_custom_props() {
ExifTool exifTool = builder
.withPath(path)
.withConfig(configPath)
.withExecutor(executor)
.enableStayOpen()
.build();

assertThat(exifTool).extracting("path").isEqualTo(path);
assertThat(exifTool).extracting("executor").isEqualTo(executor);
assertThat(exifTool).extracting("strategy").isExactlyInstanceOf(StayOpenStrategy.class);
assertThat(exifTool).extracting("strategy").extracting("configPath").isEqualTo(configPath);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

@SuppressWarnings("resource")
class DefaultStrategyTest {
private final String configPath = "config.config";

@Test
void it_should_execute_command() throws Exception {
Expand All @@ -58,6 +59,45 @@ void it_should_execute_command() throws Exception {
assertThat(cmd.getArguments()).hasSameSizeAs(expectedArguments).isEqualTo(expectedArguments);
}

@Test
void it_should_execute_command_with_config_path() throws Exception {
String exifTool = "exiftool";
List<String> args = asList("-S", "-n", "-XArtist", "-XComment", "-execute");
CommandExecutor executor = mock(CommandExecutor.class);
OutputHandler handler = mock(OutputHandler.class);

DefaultStrategy strategy = new DefaultStrategy();
strategy.setConfigFilePath(configPath);
strategy.execute(executor, exifTool, args, handler);

ArgumentCaptor<Command> cmdCaptor = ArgumentCaptor.forClass(Command.class);
verify(executor).execute(cmdCaptor.capture(), same(handler));

List<String> expectedArguments = new ArrayList<>();
expectedArguments.add(exifTool);
expectedArguments.add("-config");
expectedArguments.add(configPath);
expectedArguments.add("-sep");
expectedArguments.add("|>☃");
expectedArguments.addAll(args);

Command cmd = cmdCaptor.getValue();
assertThat(cmd.getArguments()).hasSameSizeAs(expectedArguments).isEqualTo(expectedArguments);
}


@Test
void it_should_not_have_config_path_if_none_is_set() {
assertThat(new DefaultStrategy()).extracting("configPath").isNull();
}

@Test
void it_should_set_config_path() {
DefaultStrategy strategy = new DefaultStrategy();
strategy.setConfigFilePath(configPath);
assertThat(strategy).extracting("configPath").isEqualTo(configPath);
}

@Test
void it_should_never_be_running() {
DefaultStrategy strategy = new DefaultStrategy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class PoolStrategyTest {

private CommandExecutor executor;
private String exifTool;
private String configPath;
private List<String> arguments;
private OutputHandler handler;

Expand All @@ -60,6 +61,7 @@ class PoolStrategyTest {
void setUp() {
executor = mock(CommandExecutor.class);
exifTool = "exiftool";
configPath = "config.config";
arguments = singletonList("-ver");
handler = mock(OutputHandler.class);
}
Expand Down Expand Up @@ -129,6 +131,31 @@ void it_should_check_that_version_is_supported() {
assertThat(pool.isSupported(version)).isTrue();
}

@Test
void it_should_not_set_config_path_if_none_is_set() {
ExecutionStrategy s1 = mock(ExecutionStrategy.class);
ExecutionStrategy s2 = mock(ExecutionStrategy.class);
Collection<ExecutionStrategy> strategies = asList(s1, s2);

pool = new PoolStrategy(strategies);

verify(s1, never()).setConfigFilePath(configPath);
verify(s2, never()).setConfigFilePath(configPath);
}

@Test
void it_should_set_config_path() {
ExecutionStrategy s1 = mock(ExecutionStrategy.class);
ExecutionStrategy s2 = mock(ExecutionStrategy.class);
Collection<ExecutionStrategy> strategies = asList(s1, s2);

pool = new PoolStrategy(strategies);
pool.setConfigFilePath(configPath);

verify(s1).setConfigFilePath(configPath);
verify(s2).setConfigFilePath(configPath);
}

@Test
void it_should_execute_strategies_in_parallel() throws Exception {
CountDownLatch executionLock = new CountDownLatch(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class StayOpenStrategyTest {
private OutputHandler outputHandler;

private String exifTool;
private String configPath;
private List<String> args;
private StayOpenStrategy strategy;

Expand All @@ -62,6 +63,7 @@ void setUp() throws Exception {
executor = mock(CommandExecutor.class);
outputHandler = mock(OutputHandler.class);
exifTool = "exiftool";
configPath = "config.config";

// Mock withExecutor
when(executor.start(any(Command.class))).thenReturn(process);
Expand All @@ -87,6 +89,14 @@ void it_should_create_stay_open_strategy() {
strategy = new StayOpenStrategy(scheduler);
assertThat(strategy).extracting("scheduler").isSameAs(scheduler);
assertThat(strategy).extracting("process").isNull();
assertThat(strategy).extracting("configPath").isNull();
}

@Test
void it_should_set_config_path() {
StayOpenStrategy strategy = new StayOpenStrategy(scheduler);
strategy.setConfigFilePath(configPath);
assertThat(strategy).extracting("configPath").isEqualTo(configPath);
}

@SuppressWarnings("unchecked")
Expand All @@ -111,6 +121,31 @@ void it_should_execute_command() throws Exception {
verifyExecutionArguments(argsCaptor);
}

@Test
void it_should_execute_command_with_config_path() throws Exception {
strategy = new StayOpenStrategy(scheduler);
strategy.setConfigFilePath(configPath);
strategy.execute(executor, exifTool, args, outputHandler);

ArgumentCaptor<Command> cmdCaptor = ArgumentCaptor.forClass(Command.class);
ArgumentCaptor<List<String>> argsCaptor = ArgumentCaptor.forClass(List.class);
InOrder inOrder = inOrder(scheduler, executor, process);
inOrder.verify(executor).start(cmdCaptor.capture());
inOrder.verify(scheduler).stop();
inOrder.verify(scheduler).start(any(Runnable.class));
inOrder.verify(process).write(argsCaptor.capture());
inOrder.verify(process).flush();
inOrder.verify(process).read(any(OutputHandler.class));

assertThat(strategy).extracting("process").isSameAs(process);

Command startCmd = cmdCaptor.getValue();
assertThat(startCmd.getArguments()).hasSize(9).containsExactly(
exifTool, "-config", configPath, "-stay_open", "True", "-sep", "|>☃", "-@", "-"
);
verifyExecutionArguments(argsCaptor);
}

@SuppressWarnings("unchecked")
@Test
void it_should_not_start_process_twice_if_it_is_running() throws Exception {
Expand Down
Loading
Loading