diff --git a/CHANGES.txt b/CHANGES.txt
index 313529351c9..6596344f518 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -364,6 +364,10 @@ New Features
85. SOLR-1478: Enable sort by Lucene docid. (ehatcher)
+86. SOLR-1449: Add elements to solrconfig.xml to specifying additional
+ classpath directories and regular expressions. (hossman via yonik)
+
+
Optimizations
----------------------
1. SOLR-374: Use IndexReader.reopen to save resources by re-using parts of the
diff --git a/build.xml b/build.xml
index b602679438d..383d10e3be6 100644
--- a/build.xml
+++ b/build.xml
@@ -205,7 +205,7 @@
-
+
@@ -374,11 +374,7 @@
description="Runs the core unit tests."
depends="test-core, test-contrib" />
-
-
-
-
-
+
diff --git a/contrib/clustering/build.xml b/contrib/clustering/build.xml
index 185fdcb63d5..b9a18f07024 100644
--- a/contrib/clustering/build.xml
+++ b/contrib/clustering/build.xml
@@ -48,6 +48,10 @@
+
+
@@ -112,17 +116,11 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/contrib/clustering/example/conf/solrconfig.xml b/contrib/clustering/example/conf/solrconfig.xml
index b922a59d42a..dff5e38846a 100644
--- a/contrib/clustering/example/conf/solrconfig.xml
+++ b/contrib/clustering/example/conf/solrconfig.xml
@@ -26,6 +26,13 @@
-->
${solr.abortOnConfigurationError:true}
+
+
+
+
+
@@ -555,4 +562,4 @@
-->
-
\ No newline at end of file
+
diff --git a/contrib/dataimporthandler/build.xml b/contrib/dataimporthandler/build.xml
index b9f5c0d1415..b2832a79acb 100644
--- a/contrib/dataimporthandler/build.xml
+++ b/contrib/dataimporthandler/build.xml
@@ -73,6 +73,9 @@
+
@@ -197,14 +200,11 @@
-
-
-
-
-
-
-
-
+
+
diff --git a/contrib/extraction/build.xml b/contrib/extraction/build.xml
index 01aa46b7339..471389a1e86 100644
--- a/contrib/extraction/build.xml
+++ b/contrib/extraction/build.xml
@@ -114,13 +114,7 @@
-
-
-
-
-
-
-
+
@@ -142,4 +136,4 @@
-
\ No newline at end of file
+
diff --git a/example/example-DIH/solr/mail/conf/solrconfig.xml b/example/example-DIH/solr/mail/conf/solrconfig.xml
index c8c636dd751..25b93bb9631 100644
--- a/example/example-DIH/solr/mail/conf/solrconfig.xml
+++ b/example/example-DIH/solr/mail/conf/solrconfig.xml
@@ -25,6 +25,9 @@
-Dsolr.abortOnConfigurationError=false
-->
${solr.abortOnConfigurationError:true}
+
+
+
diff --git a/example/solr/README.txt b/example/solr/README.txt
index f39ad34db07..6bd90efb28a 100644
--- a/example/solr/README.txt
+++ b/example/solr/README.txt
@@ -45,7 +45,9 @@ The Solr Home directory typically contains the following subdirectories...
This directory is optional. If it exists, Solr will load any Jars
found in this directory and use them to resolve any "plugins"
specified in your solrconfig.xml or schema.xml (ie: Analyzers,
- Request Handlers, etc...)
+ Request Handlers, etc...). Alternatively you can use the
+ syntax in solrconfig.xml to direct Solr to your plugins. See the
+ example solrconfig.xml file for details.
bin/
This directory is optional. It is the default location used for
diff --git a/example/solr/conf/solrconfig.xml b/example/solr/conf/solrconfig.xml
index e2c0a48e054..efa0fdf9eb0 100755
--- a/example/solr/conf/solrconfig.xml
+++ b/example/solr/conf/solrconfig.xml
@@ -32,6 +32,36 @@
-->
${solr.abortOnConfigurationError:true}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/common/org/apache/solr/common/util/FileUtils.java b/src/common/org/apache/solr/common/util/FileUtils.java
index 5f893646fca..61341a07690 100644
--- a/src/common/org/apache/solr/common/util/FileUtils.java
+++ b/src/common/org/apache/solr/common/util/FileUtils.java
@@ -37,7 +37,7 @@ public class FileUtils {
* of "base")
*
*/
- public static File resolvePath(File base, String path) throws IOException {
+ public static File resolvePath(File base, String path) {
File r = new File(path);
return r.isAbsolute() ? r : new File(base, path);
}
diff --git a/src/common/org/apache/solr/common/util/RegexFileFilter.java b/src/common/org/apache/solr/common/util/RegexFileFilter.java
new file mode 100644
index 00000000000..b808f12322f
--- /dev/null
+++ b/src/common/org/apache/solr/common/util/RegexFileFilter.java
@@ -0,0 +1,43 @@
+/**
+ * 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.solr.common.util;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.regex.*;
+
+/**
+ * Accepts any file whose name matches the pattern
+ * @version $Id$
+ */
+public final class RegexFileFilter implements FileFilter {
+
+ final Pattern pattern;
+ public RegexFileFilter(String regex) {
+ this(Pattern.compile(regex));
+ }
+ public RegexFileFilter(Pattern regex) {
+ pattern = regex;
+ }
+ public boolean accept(File f) {
+ return pattern.matcher(f.getName()).matches();
+ }
+ public String toString() {
+ return "regex:" + pattern.toString();
+ }
+}
diff --git a/src/java/org/apache/solr/core/SolrConfig.java b/src/java/org/apache/solr/core/SolrConfig.java
index d05e20b4174..715fdd25fc3 100644
--- a/src/java/org/apache/solr/core/SolrConfig.java
+++ b/src/java/org/apache/solr/core/SolrConfig.java
@@ -17,6 +17,8 @@
package org.apache.solr.core;
+import org.apache.solr.common.util.DOMUtil;
+import org.apache.solr.common.util.RegexFileFilter;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.handler.PingRequestHandler;
import org.apache.solr.handler.component.SearchComponent;
@@ -49,6 +51,7 @@ import javax.xml.xpath.XPathConstants;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
+import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
@@ -125,6 +128,7 @@ public class SolrConfig extends Config {
SolrConfig(SolrResourceLoader loader, String name, InputStream is)
throws ParserConfigurationException, IOException, SAXException {
super(loader, name, is, "/config/");
+ initLibs();
defaultIndexConfig = new SolrIndexConfig(this, null, null);
mainIndexConfig = new SolrIndexConfig(this, "mainIndex", defaultIndexConfig);
reopenReaders = getBool("mainIndex/reopenReaders", true);
@@ -425,4 +429,31 @@ public class SolrConfig extends Config {
List result = pluginStore.get(type);
return result == null || result.isEmpty() ? null: result.get(0);
}
+
+ private void initLibs() {
+
+ NodeList nodes = (NodeList) evaluate("lib", XPathConstants.NODESET);
+ if (nodes==null || nodes.getLength()==0)
+ return;
+
+ log.info("Adding specified lib dirs to ClassLoader");
+
+ for (int i=0; iMUST
+ * only be called prior to using this ResourceLoader to get any resources, otherwise
+ * it's behavior will be non-deterministic.
+ *
+ * @param baseDir base directory whose children (either jars or directories of
+ * classes) will be in the classpath, will be resolved relative
+ * the instance dir.
+ * @param filter The filter files must satisfy, if null all files will be accepted.
+ */
+ void addToClassLoader(final String baseDir, final FileFilter filter) {
+ File base = FileUtils.resolvePath(new File(getInstanceDir()), baseDir);
+ this.classLoader = replaceClassLoader(classLoader, base, filter);
+ }
+
+ /**
+ * Adds the specific file/dir specified to the ClassLoader used by this
+ * ResourceLoader. This method MUST
+ * only be called prior to using this ResourceLoader to get any resources, otherwise
+ * it's behavior will be non-deterministic.
+ *
+ * @param path A jar file (or directory of classes) to be added to the classpath,
+ * will be resolved relative the instance dir.
+ */
+ void addToClassLoader(final String path) {
+ final File file = FileUtils.resolvePath(new File(getInstanceDir()), path);
+ if (file.canRead()) {
+ this.classLoader = replaceClassLoader(classLoader, file.getParentFile(),
+ new FileFilter() {
+ public boolean accept(File pathname) {
+ return pathname.equals(file);
+ }
+ });
+ } else {
+ log.error("Can't find (or read) file to add to classloader: " + file);
+ }
+ }
+
+ private static URLClassLoader replaceClassLoader(final URLClassLoader oldLoader,
+ final File base,
+ final FileFilter filter) {
+ if (null != base && base.canRead() && base.isDirectory()) {
+ File[] files = base.listFiles(filter);
+
+ if (null == files || 0 == files.length) return oldLoader;
+
+ URL[] oldElements = oldLoader.getURLs();
+ URL[] elements = new URL[oldElements.length + files.length];
+ System.arraycopy(oldElements, 0, elements, 0, oldElements.length);
+
+ for (int j = 0; j < files.length; j++) {
+ try {
+ URL element = files[j].toURI().normalize().toURL();
+ log.info("Adding '" + element.toString() + "' to classloader");
+ elements[oldElements.length + j] = element;
+ } catch (MalformedURLException e) {
+ SolrException.log(log, "Can't add element to classloader: " + files[j], e);
+ }
+ }
+ return URLClassLoader.newInstance(elements, oldLoader.getParent());
+ }
+ // are we still here?
+ return oldLoader;
+ }
+
+ /**
+ * Convenience method for getting a new ClassLoader using all files found
+ * in the specified lib directory.
+ */
+ static URLClassLoader createClassLoader(final File libDir, ClassLoader parent) {
+ if ( null == parent ) {
+ parent = Thread.currentThread().getContextClassLoader();
+ }
+ return replaceClassLoader(URLClassLoader.newInstance(new URL[0], parent),
+ libDir, null);
+ }
+
public SolrResourceLoader( String instanceDir )
{
this( instanceDir, null, null );
@@ -553,6 +615,4 @@ public class SolrResourceLoader implements ResourceLoader
}
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, builder.toString() );
}
-
-
}
diff --git a/src/test/org/apache/solr/core/TestConfig.java b/src/test/org/apache/solr/core/TestConfig.java
index a664e4a3fdd..539e1af95c5 100644
--- a/src/test/org/apache/solr/core/TestConfig.java
+++ b/src/test/org/apache/solr/core/TestConfig.java
@@ -1,120 +1,146 @@
-/**
- * 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.solr.core;
-
-import org.apache.lucene.index.IndexWriter;
-import org.apache.solr.handler.admin.ShowFileRequestHandler;
-import org.apache.solr.search.SolrIndexReader;
-import org.apache.solr.search.SolrIndexSearcher;
-import org.apache.solr.update.DirectUpdateHandler2;
-import org.apache.solr.update.SolrIndexConfig;
-import org.apache.solr.util.AbstractSolrTestCase;
-import org.apache.solr.util.RefCounted;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import javax.xml.xpath.XPathConstants;
-import java.io.IOException;
-
-public class TestConfig extends AbstractSolrTestCase {
-
- public String getSchemaFile() {
- return "schema.xml";
- }
-
- //public String getSolrConfigFile() { return "solrconfig.xml"; }
- public String getSolrConfigFile() {
- return "solrconfig-termindex.xml";
- }
-
- public void testJavaProperty() {
- // property values defined in build.xml
-
- String s = solrConfig.get("propTest");
- assertEquals("prefix-proptwo-suffix", s);
-
- s = solrConfig.get("propTest/@attr1", "default");
- assertEquals("propone-${literal}", s);
-
- s = solrConfig.get("propTest/@attr2", "default");
- assertEquals("default-from-config", s);
-
- s = solrConfig.get("propTest[@attr2='default-from-config']", "default");
- assertEquals("prefix-proptwo-suffix", s);
-
- NodeList nl = (NodeList) solrConfig.evaluate("propTest", XPathConstants.NODESET);
- assertEquals(1, nl.getLength());
- assertEquals("prefix-proptwo-suffix", nl.item(0).getTextContent());
-
- Node node = solrConfig.getNode("propTest", true);
- assertEquals("prefix-proptwo-suffix", node.getTextContent());
- }
-
- public void testLucene23Upgrades() throws Exception {
- double bufferSize = solrConfig.getDouble("indexDefaults/ramBufferSizeMB");
- assertTrue(bufferSize + " does not equal: " + 32, bufferSize == 32);
- String mergePolicy = solrConfig.get("indexDefaults/mergePolicy/@class");
- assertTrue(mergePolicy + " is not equal to " + SolrIndexConfig.DEFAULT_MERGE_POLICY_CLASSNAME, mergePolicy.equals(SolrIndexConfig.DEFAULT_MERGE_POLICY_CLASSNAME) == true);
- String mergeSched = solrConfig.get("indexDefaults/mergeScheduler/@class");
- assertTrue(mergeSched + " is not equal to " + SolrIndexConfig.DEFAULT_MERGE_SCHEDULER_CLASSNAME, mergeSched.equals(SolrIndexConfig.DEFAULT_MERGE_SCHEDULER_CLASSNAME) == true);
- boolean luceneAutoCommit = solrConfig.getBool("indexDefaults/luceneAutoCommit");
- assertTrue(luceneAutoCommit + " does not equal: " + false, luceneAutoCommit == false);
- }
-
- // sometime if the config referes to old things, it must be replaced with new stuff
- public void testAutomaticDeprecationSupport() {
- // make sure the "admin/file" handler is registered
- ShowFileRequestHandler handler = (ShowFileRequestHandler) h.getCore().getRequestHandler("/admin/file");
- assertTrue("file handler should have been automatically registered", handler != null);
-
- //System.out.println( handler.getHiddenFiles() );
- // should not contain: solrconfig.xml scheam.xml admin-extra.html
- assertFalse(handler.getHiddenFiles().contains("scheam.xml".toUpperCase()));
- assertTrue(handler.getHiddenFiles().contains("PROTWORDS.TXT"));
- }
-
- public void testTermIndexInterval() throws Exception {
- class ExposeWriterHandler extends DirectUpdateHandler2 {
- public ExposeWriterHandler() throws IOException {
- super(h.getCore());
- }
-
- public IndexWriter getWriter() throws IOException {
- forceOpenWriter();
- return writer;
- }
- }
-
- IndexWriter writer = new ExposeWriterHandler().getWriter();
- int interval = writer.getTermIndexInterval();
- assertEquals(256, interval);
- }
-
- public void testTermIndexDivisor() throws Exception {
- IndexReaderFactory irf = h.getCore().getIndexReaderFactory();
- StandardIndexReaderFactory sirf = (StandardIndexReaderFactory) irf;
- assertEquals(12, sirf.termInfosIndexDivisor);
- RefCounted refCounted = h.getCore().getSearcher();
- SolrIndexReader solrReader = refCounted.get().getReader();
- assertEquals(12, solrReader.getTermInfosIndexDivisor());
- }
-
-
-}
-
-
+/**
+ * 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.solr.core;
+
+import org.apache.lucene.index.IndexWriter;
+import org.apache.solr.handler.admin.ShowFileRequestHandler;
+import org.apache.solr.search.SolrIndexReader;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.update.DirectUpdateHandler2;
+import org.apache.solr.update.SolrIndexConfig;
+import org.apache.solr.util.AbstractSolrTestCase;
+import org.apache.solr.util.RefCounted;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.xpath.XPathConstants;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class TestConfig extends AbstractSolrTestCase {
+
+ public String getSchemaFile() {
+ return "schema.xml";
+ }
+
+ //public String getSolrConfigFile() { return "solrconfig.xml"; }
+ public String getSolrConfigFile() {
+ return "solrconfig-termindex.xml";
+ }
+
+ public void testLib() throws IOException {
+ SolrResourceLoader loader = h.getCore().getResourceLoader();
+ InputStream data = null;
+ String[] expectedFiles = new String[] { "empty-file-main-lib.txt",
+ "empty-file-a1.txt",
+ "empty-file-a2.txt",
+ "empty-file-b1.txt",
+ "empty-file-b2.txt",
+ "empty-file-c1.txt" };
+ for (String f : expectedFiles) {
+ data = loader.openResource(f);
+ assertNotNull("Should have found file " + f, data);
+ data.close();
+ }
+ String[] unexpectedFiles = new String[] { "empty-file-c2.txt",
+ "empty-file-d2.txt" };
+ for (String f : unexpectedFiles) {
+ data = null;
+ try {
+ data = loader.openResource(f);
+ } catch (Exception e) { /* :NOOP: (un)expected */ }
+ assertNull("should not have been able to find " + f, data);
+ }
+ }
+
+ public void testJavaProperty() {
+ // property values defined in build.xml
+
+ String s = solrConfig.get("propTest");
+ assertEquals("prefix-proptwo-suffix", s);
+
+ s = solrConfig.get("propTest/@attr1", "default");
+ assertEquals("propone-${literal}", s);
+
+ s = solrConfig.get("propTest/@attr2", "default");
+ assertEquals("default-from-config", s);
+
+ s = solrConfig.get("propTest[@attr2='default-from-config']", "default");
+ assertEquals("prefix-proptwo-suffix", s);
+
+ NodeList nl = (NodeList) solrConfig.evaluate("propTest", XPathConstants.NODESET);
+ assertEquals(1, nl.getLength());
+ assertEquals("prefix-proptwo-suffix", nl.item(0).getTextContent());
+
+ Node node = solrConfig.getNode("propTest", true);
+ assertEquals("prefix-proptwo-suffix", node.getTextContent());
+ }
+
+ public void testLucene23Upgrades() throws Exception {
+ double bufferSize = solrConfig.getDouble("indexDefaults/ramBufferSizeMB");
+ assertTrue(bufferSize + " does not equal: " + 32, bufferSize == 32);
+ String mergePolicy = solrConfig.get("indexDefaults/mergePolicy/@class");
+ assertTrue(mergePolicy + " is not equal to " + SolrIndexConfig.DEFAULT_MERGE_POLICY_CLASSNAME, mergePolicy.equals(SolrIndexConfig.DEFAULT_MERGE_POLICY_CLASSNAME) == true);
+ String mergeSched = solrConfig.get("indexDefaults/mergeScheduler/@class");
+ assertTrue(mergeSched + " is not equal to " + SolrIndexConfig.DEFAULT_MERGE_SCHEDULER_CLASSNAME, mergeSched.equals(SolrIndexConfig.DEFAULT_MERGE_SCHEDULER_CLASSNAME) == true);
+ boolean luceneAutoCommit = solrConfig.getBool("indexDefaults/luceneAutoCommit");
+ assertTrue(luceneAutoCommit + " does not equal: " + false, luceneAutoCommit == false);
+ }
+
+ // sometime if the config referes to old things, it must be replaced with new stuff
+ public void testAutomaticDeprecationSupport() {
+ // make sure the "admin/file" handler is registered
+ ShowFileRequestHandler handler = (ShowFileRequestHandler) h.getCore().getRequestHandler("/admin/file");
+ assertTrue("file handler should have been automatically registered", handler != null);
+
+ //System.out.println( handler.getHiddenFiles() );
+ // should not contain: solrconfig.xml scheam.xml admin-extra.html
+ assertFalse(handler.getHiddenFiles().contains("scheam.xml".toUpperCase()));
+ assertTrue(handler.getHiddenFiles().contains("PROTWORDS.TXT"));
+ }
+
+ public void testTermIndexInterval() throws Exception {
+ class ExposeWriterHandler extends DirectUpdateHandler2 {
+ public ExposeWriterHandler() throws IOException {
+ super(h.getCore());
+ }
+
+ public IndexWriter getWriter() throws IOException {
+ forceOpenWriter();
+ return writer;
+ }
+ }
+
+ IndexWriter writer = new ExposeWriterHandler().getWriter();
+ int interval = writer.getTermIndexInterval();
+ assertEquals(256, interval);
+ }
+
+ public void testTermIndexDivisor() throws Exception {
+ IndexReaderFactory irf = h.getCore().getIndexReaderFactory();
+ StandardIndexReaderFactory sirf = (StandardIndexReaderFactory) irf;
+ assertEquals(12, sirf.termInfosIndexDivisor);
+ RefCounted refCounted = h.getCore().getSearcher();
+ SolrIndexReader solrReader = refCounted.get().getReader();
+ assertEquals(12, solrReader.getTermInfosIndexDivisor());
+ }
+
+
+}
+
+
diff --git a/src/test/test-files/lib-dirs/README b/src/test/test-files/lib-dirs/README
new file mode 100644
index 00000000000..b7ca5b834f4
--- /dev/null
+++ b/src/test/test-files/lib-dirs/README
@@ -0,0 +1,18 @@
+
+
+Items under this directory are used by TestConfig.testLibs()
diff --git a/src/test/test-files/lib-dirs/a/a1/empty-file-a1.txt b/src/test/test-files/lib-dirs/a/a1/empty-file-a1.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/a/a1/empty-file-a1.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/a/a2/empty-file-a2.txt b/src/test/test-files/lib-dirs/a/a2/empty-file-a2.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/a/a2/empty-file-a2.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/b/b1/empty-file-b1.txt b/src/test/test-files/lib-dirs/b/b1/empty-file-b1.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/b/b1/empty-file-b1.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/b/b2/empty-file-b2.txt b/src/test/test-files/lib-dirs/b/b2/empty-file-b2.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/b/b2/empty-file-b2.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/c/c1/empty-file-c1.txt b/src/test/test-files/lib-dirs/c/c1/empty-file-c1.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/c/c1/empty-file-c1.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/c/c2/empty-file-c2.txt b/src/test/test-files/lib-dirs/c/c2/empty-file-c2.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/c/c2/empty-file-c2.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/d/d1/empty-file-d1.txt b/src/test/test-files/lib-dirs/d/d1/empty-file-d1.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/d/d1/empty-file-d1.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/lib-dirs/d/d2/empty-file-d2.txt b/src/test/test-files/lib-dirs/d/d2/empty-file-d2.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/lib-dirs/d/d2/empty-file-d2.txt
@@ -0,0 +1 @@
+
diff --git a/src/test/test-files/solr/conf/solrconfig-termindex.xml b/src/test/test-files/solr/conf/solrconfig-termindex.xml
index 41b2a09267c..65298ae5c99 100644
--- a/src/test/test-files/solr/conf/solrconfig-termindex.xml
+++ b/src/test/test-files/solr/conf/solrconfig-termindex.xml
@@ -24,6 +24,12 @@
+
+
+
+
+
+
+
+Items under this directory are used by TestConfig.testLibs()
diff --git a/src/test/test-files/solr/lib/classes/empty-file-main-lib.txt b/src/test/test-files/solr/lib/classes/empty-file-main-lib.txt
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/test/test-files/solr/lib/classes/empty-file-main-lib.txt
@@ -0,0 +1 @@
+