Fix for SOLR-4791, sharedLib in solr.xml doesn't work

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1480228 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erick Erickson 2013-05-08 11:34:23 +00:00
parent a039f476c8
commit dcc27e14b6
5 changed files with 122 additions and 34 deletions

View File

@ -270,7 +270,7 @@ public class CoreContainer
if (libDir != null) { if (libDir != null) {
File f = FileUtils.resolvePath(new File(dir), libDir); File f = FileUtils.resolvePath(new File(dir), libDir);
log.info("loading shared library: " + f.getAbsolutePath()); log.info("loading shared library: " + f.getAbsolutePath());
loader.addToClassLoader(libDir); loader.addToClassLoader(libDir, null, false);
loader.reloadLuceneSPI(); loader.reloadLuceneSPI();
} }

View File

@ -19,9 +19,9 @@ package org.apache.solr.core;
import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.IndexSchemaFactory; import org.apache.solr.schema.IndexSchemaFactory;
import org.apache.solr.util.DOMUtil; import org.apache.solr.util.DOMUtil;
import org.apache.solr.util.FileUtils;
import org.apache.solr.util.RegexFileFilter; import org.apache.solr.util.RegexFileFilter;
import org.apache.solr.handler.component.SearchComponent; import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestHandler;
@ -51,6 +51,7 @@ import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
import java.io.File;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -453,6 +454,7 @@ public class SolrConfig extends Config {
if (nodes == null || nodes.getLength() == 0) return; if (nodes == null || nodes.getLength() == 0) return;
log.info("Adding specified lib dirs to ClassLoader"); log.info("Adding specified lib dirs to ClassLoader");
SolrResourceLoader loader = getResourceLoader();
try { try {
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
@ -464,16 +466,22 @@ public class SolrConfig extends Config {
// :TODO: add support for a simpler 'glob' mutually exclusive of regex // :TODO: add support for a simpler 'glob' mutually exclusive of regex
String regex = DOMUtil.getAttr(node, "regex"); String regex = DOMUtil.getAttr(node, "regex");
FileFilter filter = (null == regex) ? null : new RegexFileFilter(regex); FileFilter filter = (null == regex) ? null : new RegexFileFilter(regex);
getResourceLoader().addToClassLoader(baseDir, filter, false); loader.addToClassLoader(baseDir, filter, false);
} else if (null != path) { } else if (null != path) {
getResourceLoader().addToClassLoader(path); final File file = FileUtils.resolvePath(new File(loader.getInstanceDir()), path);
loader.addToClassLoader(file.getParent(), new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.equals(file);
}
}, false);
} else { } else {
throw new RuntimeException( throw new RuntimeException(
"lib: missing mandatory attributes: 'dir' or 'path'"); "lib: missing mandatory attributes: 'dir' or 'path'");
} }
} }
} finally { } finally {
getResourceLoader().reloadLuceneSPI(); loader.reloadLuceneSPI();
} }
} }
} }

View File

@ -152,9 +152,11 @@ public class SolrResourceLoader implements ResourceLoader
File base = FileUtils.resolvePath(new File(getInstanceDir()), baseDir); File base = FileUtils.resolvePath(new File(getInstanceDir()), baseDir);
if (base != null && base.exists() && base.isDirectory()) { if (base != null && base.exists() && base.isDirectory()) {
File[] files = base.listFiles(filter); File[] files = base.listFiles(filter);
if (!quiet && (files == null || files.length == 0)) { if (files == null || files.length == 0) {
log.warn("No files added to classloader from lib: " if (!quiet) {
+ baseDir + " (resolved as: " + base.getAbsolutePath() + ")."); log.warn("No files added to classloader from lib: "
+ baseDir + " (resolved as: " + base.getAbsolutePath() + ").");
}
} else { } else {
this.classLoader = replaceClassLoader(classLoader, base, filter); this.classLoader = replaceClassLoader(classLoader, base, filter);
} }
@ -166,34 +168,9 @@ public class SolrResourceLoader implements ResourceLoader
} }
} }
/**
* Adds the specific file/dir specified to the ClassLoader used by this
* ResourceLoader. This method <b>MUST</b>
* only be called prior to using this ResourceLoader to get any resources, otherwise
* it's behavior will be non-deterministic. You also have to {link #reloadLuceneSPI()}
* before using this ResourceLoader.
*
* @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() {
@Override
public boolean accept(File pathname) {
return pathname.equals(file);
}
});
} else {
log.error("Can't find (or read) file to add to classloader: " + file);
}
}
/** /**
* Reloads all Lucene SPI implementations using the new classloader. * Reloads all Lucene SPI implementations using the new classloader.
* This method must be called after {@link #addToClassLoader(String)} * This method must be called after {@link #addToClassLoader(String, FileFilter, boolean)}
* and {@link #addToClassLoader(String,FileFilter,boolean)} before using * and {@link #addToClassLoader(String,FileFilter,boolean)} before using
* this ResourceLoader. * this ResourceLoader.
*/ */

View File

@ -22,6 +22,7 @@ import junit.framework.Assert;
import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.analysis.core.KeywordTokenizerFactory; import org.apache.lucene.analysis.core.KeywordTokenizerFactory;
import org.apache.lucene.analysis.ngram.NGramFilterFactory; import org.apache.lucene.analysis.ngram.NGramFilterFactory;
import org.apache.lucene.util._TestUtil;
import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException;
import org.apache.solr.handler.admin.LukeRequestHandler; import org.apache.solr.handler.admin.LukeRequestHandler;
import org.apache.solr.handler.component.FacetComponent; import org.apache.solr.handler.component.FacetComponent;
@ -30,11 +31,15 @@ import org.apache.lucene.analysis.util.ResourceLoaderAware;
import org.apache.solr.util.plugin.SolrCoreAware; import org.apache.solr.util.plugin.SolrCoreAware;
import java.io.File; import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.CharacterCodingException; import java.nio.charset.CharacterCodingException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
public class ResourceLoaderTest extends LuceneTestCase public class ResourceLoaderTest extends LuceneTestCase
{ {
@ -131,4 +136,50 @@ public class ResourceLoaderTest extends LuceneTestCase
assertTrue(expected.getCause() instanceof CharacterCodingException); assertTrue(expected.getCause() instanceof CharacterCodingException);
} }
} }
public void testClassLoaderLibs() throws Exception {
File tmpRoot = _TestUtil.getTempDir("testClassLoaderLibs");
File lib = new File(tmpRoot, "lib");
lib.mkdirs();
JarOutputStream jar1 = new JarOutputStream(new FileOutputStream(new File(lib, "jar1.jar")));
jar1.putNextEntry(new JarEntry("aLibFile"));
jar1.closeEntry();
jar1.close();
File otherLib = new File(tmpRoot, "otherLib");
otherLib.mkdirs();
JarOutputStream jar2 = new JarOutputStream(new FileOutputStream(new File(otherLib, "jar2.jar")));
jar2.putNextEntry(new JarEntry("explicitFile"));
jar2.closeEntry();
jar2.close();
JarOutputStream jar3 = new JarOutputStream(new FileOutputStream(new File(otherLib, "jar3.jar")));
jar3.putNextEntry(new JarEntry("otherFile"));
jar3.closeEntry();
jar3.close();
SolrResourceLoader loader = new SolrResourceLoader(tmpRoot.getAbsolutePath());
// ./lib is accessible by default
assertNotNull(loader.getClassLoader().getResource("aLibFile"));
// file filter works (and doesn't add other files in the same dir)
final File explicitFileJar = new File(otherLib, "jar2.jar").getAbsoluteFile();
loader.addToClassLoader("otherLib",
new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.equals(explicitFileJar);
}
}, false);
assertNotNull(loader.getClassLoader().getResource("explicitFile"));
assertNull(loader.getClassLoader().getResource("otherFile"));
// null file filter means accept all (making otherFile accessible)
loader.addToClassLoader("otherLib", null, false);
assertNotNull(loader.getClassLoader().getResource("otherFile"));
}
} }

View File

@ -24,11 +24,14 @@ import java.io.IOException;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util._TestUtil;
import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.SolrTestCaseJ4;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -335,6 +338,55 @@ public class TestCoreContainer extends SolrTestCaseJ4 {
} }
} }
@Test
public void testSharedLib() throws Exception {
File tmpRoot = _TestUtil.getTempDir("testSharedLib");
File lib = new File(tmpRoot, "lib");
lib.mkdirs();
JarOutputStream jar1 = new JarOutputStream(new FileOutputStream(new File(lib, "jar1.jar")));
jar1.putNextEntry(new JarEntry("defaultSharedLibFile"));
jar1.closeEntry();
jar1.close();
File customLib = new File(tmpRoot, "customLib");
customLib.mkdirs();
JarOutputStream jar2 = new JarOutputStream(new FileOutputStream(new File(customLib, "jar2.jar")));
jar2.putNextEntry(new JarEntry("customSharedLibFile"));
jar2.closeEntry();
jar2.close();
FileUtils.writeStringToFile(new File(tmpRoot, "default-lib-solr.xml"), "<solr><cores/></solr>", "UTF-8");
FileUtils.writeStringToFile(new File(tmpRoot, "explicit-lib-solr.xml"), "<solr sharedLib=\"lib\"><cores/></solr>", "UTF-8");
FileUtils.writeStringToFile(new File(tmpRoot, "custom-lib-solr.xml"), "<solr sharedLib=\"customLib\"><cores/></solr>", "UTF-8");
final CoreContainer cc1 = new CoreContainer(tmpRoot.getAbsolutePath());
cc1.load(tmpRoot.getAbsolutePath(), new File(tmpRoot, "default-lib-solr.xml"));
try {
cc1.loader.openResource("defaultSharedLibFile").close();
} finally {
cc1.shutdown();
}
final CoreContainer cc2 = new CoreContainer(tmpRoot.getAbsolutePath());
cc2.load(tmpRoot.getAbsolutePath(), new File(tmpRoot, "explicit-lib-solr.xml"));
try {
cc2.loader.openResource("defaultSharedLibFile").close();
} finally {
cc2.shutdown();
}
final CoreContainer cc3 = new CoreContainer(tmpRoot.getAbsolutePath());
cc3.load(tmpRoot.getAbsolutePath(), new File(tmpRoot, "custom-lib-solr.xml"));
try {
cc3.loader.openResource("customSharedLibFile").close();
} finally {
cc3.shutdown();
}
}
private static final String EMPTY_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + private static final String EMPTY_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"<solr persistent=\"false\">\n" + "<solr persistent=\"false\">\n" +
" <cores adminPath=\"/admin/cores\" transientCacheSize=\"32\" >\n" + " <cores adminPath=\"/admin/cores\" transientCacheSize=\"32\" >\n" +