Skip to content

Instantly share code, notes, and snippets.

@epugh
Created March 21, 2026 18:35
Show Gist options
  • Select an option

  • Save epugh/beaf36329593aa879af2d26eaa9030fd to your computer and use it in GitHub Desktop.

Select an option

Save epugh/beaf36329593aa879af2d26eaa9030fd to your computer and use it in GitHub Desktop.
fix for external paths...
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
index a29e3d0da43..7a321671c35 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
@@ -130,7 +130,7 @@ public class SolrTestCase extends LuceneTestCase {
return;
}
final Path extPath = ExternalPaths.DEFAULT_CONFIGSET;
- if (Files.isReadable(extPath /* implies exists() */) && Files.isDirectory(extPath)) {
+ if (extPath != null && Files.isReadable(extPath /* implies exists() */) && Files.isDirectory(extPath)) {
log.info(
"Setting '{}' system property to test-framework derived value of '{}'",
SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE,
diff --git a/solr/test-framework/src/java/org/apache/solr/util/ExternalPaths.java b/solr/test-framework/src/java/org/apache/solr/util/ExternalPaths.java
index 940ec58b9e9..445d97bf848 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/ExternalPaths.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/ExternalPaths.java
@@ -16,19 +16,28 @@
*/
package org.apache.solr.util;
+import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.solr.common.SolrException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Some tests need to reach outside the classpath to get certain resources (e.g. the example
* configuration). This class provides some paths to allow them to do this.
*
+ * <p>When running in external plugin projects (outside the Solr source tree), {@link #SOURCE_HOME}
+ * will be null and the path getter methods will also return null. External projects can set the
+ * system property {@code solr.test.source.home} to provide an explicit source home directory.
+ *
* @lucene.internal
*/
public class ExternalPaths {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
/**
* The main directory path for the solr source being built if it can be determined. If it can not
* be determined -- possibly because the current context is a client code base using the test
@@ -39,29 +48,116 @@ public class ExternalPaths {
*/
public static final Path SOURCE_HOME = determineSourceHome();
+ // Lazy-initialized paths to avoid NPE during class loading
+ private static volatile Path webappHome;
+ private static volatile Path defaultConfigSet;
+ private static volatile Path techproductsConfigSet;
+ private static volatile Path serverHome;
+
+ /**
+ * Returns the path to the webapp directory, or null if SOURCE_HOME is not available. This is
+ * typically used when running tests within the Solr source tree.
+ *
+ * @return Path to webapp/web directory, or null if not running in Solr source tree
+ */
+ public static Path getWebappHome() {
+ if (webappHome == null && SOURCE_HOME != null) {
+ synchronized (ExternalPaths.class) {
+ if (webappHome == null) {
+ webappHome = SOURCE_HOME.resolve("webapp/web").toAbsolutePath();
+ }
+ }
+ }
+ return webappHome;
+ }
+
+ /**
+ * Returns the path to the default configset, or null if SOURCE_HOME is not available.
+ *
+ * @return Path to default configset, or null if not running in Solr source tree
+ */
+ public static Path getDefaultConfigSet() {
+ if (defaultConfigSet == null && SOURCE_HOME != null) {
+ synchronized (ExternalPaths.class) {
+ if (defaultConfigSet == null) {
+ defaultConfigSet =
+ SOURCE_HOME.resolve("server/solr/configsets/_default/conf").toAbsolutePath();
+ }
+ }
+ }
+ return defaultConfigSet;
+ }
+
+ /**
+ * Returns the path to the techproducts configset, or null if SOURCE_HOME is not available.
+ *
+ * @return Path to techproducts configset, or null if not running in Solr source tree
+ */
+ public static Path getTechproductsConfigSet() {
+ if (techproductsConfigSet == null && SOURCE_HOME != null) {
+ synchronized (ExternalPaths.class) {
+ if (techproductsConfigSet == null) {
+ techproductsConfigSet =
+ SOURCE_HOME
+ .resolve("server/solr/configsets/sample_techproducts_configs/conf")
+ .toAbsolutePath();
+ }
+ }
+ }
+ return techproductsConfigSet;
+ }
+
/**
- * @see #SOURCE_HOME
+ * Returns the path to the server home directory, or null if SOURCE_HOME is not available.
+ *
+ * @return Path to server/solr directory, or null if not running in Solr source tree
*/
- public static Path WEBAPP_HOME = SOURCE_HOME.resolve("webapp/web").toAbsolutePath();
+ public static Path getServerHome() {
+ if (serverHome == null && SOURCE_HOME != null) {
+ synchronized (ExternalPaths.class) {
+ if (serverHome == null) {
+ serverHome = SOURCE_HOME.resolve("server/solr").toAbsolutePath();
+ }
+ }
+ }
+ return serverHome;
+ }
/**
- * @see #SOURCE_HOME
+ * Backward compatibility - deprecated in favor of getWebappHome()
+ *
+ * @deprecated Use {@link #getWebappHome()} instead. This field may be null when running in
+ * external plugin projects.
*/
- public static Path DEFAULT_CONFIGSET =
- SOURCE_HOME.resolve("server/solr/configsets/_default/conf").toAbsolutePath();
+ @Deprecated
+ public static final Path WEBAPP_HOME = getWebappHome();
/**
- * @see #SOURCE_HOME
+ * Backward compatibility - deprecated in favor of getDefaultConfigSet()
+ *
+ * @deprecated Use {@link #getDefaultConfigSet()} instead. This field may be null when running in
+ * external plugin projects.
*/
- public static Path TECHPRODUCTS_CONFIGSET =
- SOURCE_HOME
- .resolve("server/solr/configsets/sample_techproducts_configs/conf")
- .toAbsolutePath();
+ @Deprecated
+ public static final Path DEFAULT_CONFIGSET = getDefaultConfigSet();
/**
- * @see #SOURCE_HOME
+ * Backward compatibility - deprecated in favor of getTechproductsConfigSet()
+ *
+ * @deprecated Use {@link #getTechproductsConfigSet()} instead. This field may be null when
+ * running in external plugin projects.
*/
- public static Path SERVER_HOME = SOURCE_HOME.resolve("server/solr").toAbsolutePath();
+ @Deprecated
+ public static final Path TECHPRODUCTS_CONFIGSET = getTechproductsConfigSet();
+
+ /**
+ * Backward compatibility - deprecated in favor of getServerHome()
+ *
+ * @deprecated Use {@link #getServerHome()} instead. This field may be null when running in
+ * external plugin projects.
+ */
+ @Deprecated
+ public static final Path SERVER_HOME = getServerHome();
/**
* Ugly, ugly hack to determine the example home without depending on the CWD this is needed for
@@ -69,9 +165,24 @@ public class ExternalPaths {
* determined, this method returns null.
*/
static Path determineSourceHome() {
+ // Allow explicit override for external plugin projects via system property
+ String explicitSourceHome = System.getProperty("solr.test.source.home");
+ if (explicitSourceHome != null && !explicitSourceHome.isEmpty()) {
+ Path explicitPath = Path.of(explicitSourceHome);
+ if (Files.isDirectory(explicitPath)) {
+ log.info("Using explicit Solr test source home: {}", explicitPath);
+ return explicitPath.toAbsolutePath();
+ } else {
+ log.warn("Explicit solr.test.source.home is not a directory: {}", explicitPath);
+ }
+ }
+
try {
+ // Try to find solr/conf in the filesystem first
Path file = Path.of("solr/conf");
+
if (!Files.exists(file)) {
+ // Try to find it in the classpath
URL resourceUrl = ExternalPaths.class.getClassLoader().getResource("solr/conf");
if (resourceUrl != null) {
file = Path.of(resourceUrl.toURI());
@@ -82,15 +193,30 @@ public class ExternalPaths {
}
}
+ // Walk up the directory tree looking for the Solr source marker file
Path base = file.toAbsolutePath();
- while (!Files.exists(base.resolve("solr/test-framework/build.gradle")) && null != base) {
+ while (base != null && !Files.exists(base.resolve("solr/test-framework/build.gradle"))) {
base = base.getParent();
}
- return (null == base) ? null : base.resolve("solr/").toAbsolutePath();
+
+ if (base == null) {
+ log.info(
+ "Could not determine Solr source home - not running in Solr source tree. "
+ + "This is expected for external plugin projects. "
+ + "Set -Dsolr.test.source.home=/path/to/solr if you need ExternalPaths.");
+ return null;
+ }
+
+ Path sourceHome = base.resolve("solr/").toAbsolutePath();
+ log.debug("Determined Solr source home: {}", sourceHome);
+ return sourceHome;
} catch (Exception e) {
- // all bets are off
- throw new SolrException(
- SolrException.ErrorCode.SERVER_ERROR, "Failed to determine source home", e);
+ // Gracefully return null for external plugin projects instead of throwing
+ log.warn(
+ "Failed to determine Solr source home - returning null. "
+ + "This is expected for external plugin projects: {}",
+ e.toString());
+ return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment