This is an automated email from the ASF dual-hosted git repository. ndipiazza pushed a commit to branch pf4j-development-mode in repository https://gitbox.apache.org/repos/asf/tika.git
commit ce7de1d90b423d1d4a4495fa1b9b30de40a1cbca Author: Nicholas DiPiazza <[email protected]> AuthorDate: Sun Dec 21 18:34:41 2025 -0600 TIKA-4587: Add pf4j development mode support to TikaPluginManager Enables plugin development without packaging as ZIP files by supporting pf4j's DEVELOPMENT runtime mode. This allows developers to point plugin-roots directly at unpackaged plugin directories (e.g., target/classes) for faster iteration during development. Features: - Configure via system property: -Dtika.plugin.dev.mode=true - Configure via environment variable: TIKA_PLUGIN_DEV_MODE=true - Skips ZIP extraction when in development mode - Logs mode on startup for visibility - Defaults to DEPLOYMENT mode for backward compatibility This aligns with pf4j best practices documented at: https://pf4j.org/doc/development-mode.html JIRA: https://issues.apache.org/jira/browse/TIKA-4587 --- .../org/apache/tika/plugins/TikaPluginManager.java | 33 ++++++++++++- .../apache/tika/plugins/TikaPluginManagerTest.java | 57 ++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/tika-plugins-core/src/main/java/org/apache/tika/plugins/TikaPluginManager.java b/tika-plugins-core/src/main/java/org/apache/tika/plugins/TikaPluginManager.java index cd6296755..0cd635660 100644 --- a/tika-plugins-core/src/main/java/org/apache/tika/plugins/TikaPluginManager.java +++ b/tika-plugins-core/src/main/java/org/apache/tika/plugins/TikaPluginManager.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.pf4j.DefaultExtensionFinder; import org.pf4j.DefaultPluginManager; import org.pf4j.ExtensionFinder; +import org.pf4j.RuntimeMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +45,9 @@ import org.apache.tika.exception.TikaConfigException; public class TikaPluginManager extends DefaultPluginManager { private static final Logger LOG = LoggerFactory.getLogger(TikaPluginManager.class); + + private static final String DEV_MODE_PROPERTY = "tika.plugin.dev.mode"; + private static final String DEV_MODE_ENV = "TIKA_PLUGIN_DEV_MODE"; //we're only using this to convert a single path or a list of paths to a list //we don't need all the functionality of the polymorphic objectmapper in tika-serialization @@ -142,8 +146,29 @@ public class TikaPluginManager extends DefaultPluginManager { public TikaPluginManager(List<Path> pluginRoots) throws IOException { super(pluginRoots); + configureRuntimeMode(); init(); } + + private void configureRuntimeMode() { + RuntimeMode mode = isDevelopmentMode() ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT; + this.runtimeMode = mode; + if (mode == RuntimeMode.DEVELOPMENT) { + LOG.info("TikaPluginManager running in DEVELOPMENT mode"); + } + } + + private static boolean isDevelopmentMode() { + String sysProp = System.getProperty(DEV_MODE_PROPERTY); + if (sysProp != null) { + return Boolean.parseBoolean(sysProp); + } + String envVar = System.getenv(DEV_MODE_ENV); + if (envVar != null) { + return Boolean.parseBoolean(envVar); + } + return false; + } /** * Override to disable classpath scanning for extensions. @@ -163,8 +188,12 @@ public class TikaPluginManager extends DefaultPluginManager { } private void init() throws IOException { - for (Path root : pluginsRoots) { - unzip(root); + if (getRuntimeMode() == RuntimeMode.DEPLOYMENT) { + for (Path root : pluginsRoots) { + unzip(root); + } + } else { + LOG.debug("Skipping ZIP extraction in DEVELOPMENT mode"); } } diff --git a/tika-plugins-core/src/test/java/org/apache/tika/plugins/TikaPluginManagerTest.java b/tika-plugins-core/src/test/java/org/apache/tika/plugins/TikaPluginManagerTest.java new file mode 100644 index 000000000..ca492d055 --- /dev/null +++ b/tika-plugins-core/src/test/java/org/apache/tika/plugins/TikaPluginManagerTest.java @@ -0,0 +1,57 @@ +/* + * 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. + */ +package org.apache.tika.plugins; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import java.util.Collections; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.pf4j.RuntimeMode; + +public class TikaPluginManagerTest { + + @Test + public void testDefaultRuntimeModeIsDeployment(@TempDir Path tmpDir) throws Exception { + TikaPluginManager manager = new TikaPluginManager(Collections.singletonList(tmpDir)); + assertEquals(RuntimeMode.DEPLOYMENT, manager.getRuntimeMode()); + } + + @Test + public void testDevelopmentModeViaSystemProperty(@TempDir Path tmpDir) throws Exception { + System.setProperty("tika.plugin.dev.mode", "true"); + try { + TikaPluginManager manager = new TikaPluginManager(Collections.singletonList(tmpDir)); + assertEquals(RuntimeMode.DEVELOPMENT, manager.getRuntimeMode()); + } finally { + System.clearProperty("tika.plugin.dev.mode"); + } + } + + @Test + public void testDeploymentModeWhenPropertyIsFalse(@TempDir Path tmpDir) throws Exception { + System.setProperty("tika.plugin.dev.mode", "false"); + try { + TikaPluginManager manager = new TikaPluginManager(Collections.singletonList(tmpDir)); + assertEquals(RuntimeMode.DEPLOYMENT, manager.getRuntimeMode()); + } finally { + System.clearProperty("tika.plugin.dev.mode"); + } + } +}
