SOLR-7259: fix thread safety of lazy loaded plugins

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1667431 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2015-03-18 01:53:41 +00:00
parent ce472961c9
commit bf00c1a0a7
1 changed files with 35 additions and 19 deletions

View File

@ -235,7 +235,7 @@ public class PluginBag<T> implements AutoCloseable {
* subclasses may choose to lazily load the plugin * subclasses may choose to lazily load the plugin
*/ */
public static class PluginHolder<T> implements AutoCloseable { public static class PluginHolder<T> implements AutoCloseable {
protected T inst; private T inst;
protected final PluginInfo pluginInfo; protected final PluginInfo pluginInfo;
public PluginHolder(PluginInfo info) { public PluginHolder(PluginInfo info) {
@ -257,8 +257,14 @@ public class PluginBag<T> implements AutoCloseable {
@Override @Override
public void close() throws Exception { public void close() throws Exception {
if (inst != null && inst instanceof AutoCloseable) ((AutoCloseable) inst).close(); // TODO: there may be a race here. One thread can be creating a plugin
// and another thread can come along and close everything (missing the plugin
// that is in the state of being created and will probably never have close() called on it).
// can close() be called concurrently with other methods?
if (isLoaded()) {
T myInst = get();
if (myInst != null && myInst instanceof AutoCloseable) ((AutoCloseable) myInst).close();
}
} }
public String getClassName() { public String getClassName() {
@ -273,6 +279,7 @@ public class PluginBag<T> implements AutoCloseable {
* the Plugin is initialized and returned. * the Plugin is initialized and returned.
*/ */
public static class LazyPluginHolder<T> extends PluginHolder<T> { public static class LazyPluginHolder<T> extends PluginHolder<T> {
private volatile T lazyInst;
private final SolrConfig.SolrPluginInfo pluginMeta; private final SolrConfig.SolrPluginInfo pluginMeta;
protected SolrException solrException; protected SolrException solrException;
private final SolrCore core; private final SolrCore core;
@ -294,36 +301,45 @@ public class PluginBag<T> implements AutoCloseable {
} }
@Override @Override
public T get() { public boolean isLoaded() {
if (inst != null) return inst; return lazyInst != null;
if (solrException != null) throw solrException;
createInst();
registerMBean(inst, core, pluginInfo.name);
return inst;
} }
protected synchronized void createInst() { @Override
if (inst != null) return; public T get() {
if (lazyInst != null) return lazyInst;
if (solrException != null) throw solrException;
if (createInst()) {
// check if we created the instance to avoid registering it again
registerMBean(lazyInst, core, pluginInfo.name);
}
return lazyInst;
}
private synchronized boolean createInst() {
if (lazyInst != null) return false;
log.info("Going to create a new {} with {} ", pluginMeta.tag, pluginInfo.toString()); log.info("Going to create a new {} with {} ", pluginMeta.tag, pluginInfo.toString());
if (resourceLoader instanceof MemClassLoader) { if (resourceLoader instanceof MemClassLoader) {
MemClassLoader loader = (MemClassLoader) resourceLoader; MemClassLoader loader = (MemClassLoader) resourceLoader;
loader.loadJars(); loader.loadJars();
} }
Class<T> clazz = (Class<T>) pluginMeta.clazz; Class<T> clazz = (Class<T>) pluginMeta.clazz;
inst = core.createInstance(pluginInfo.className, clazz, pluginMeta.tag, null, resourceLoader); T localInst = core.createInstance(pluginInfo.className, clazz, pluginMeta.tag, null, resourceLoader);
initInstance(inst, pluginInfo, core); initInstance(localInst, pluginInfo, core);
if (inst instanceof SolrCoreAware) { if (localInst instanceof SolrCoreAware) {
SolrResourceLoader.assertAwareCompatibility(SolrCoreAware.class, inst); SolrResourceLoader.assertAwareCompatibility(SolrCoreAware.class, localInst);
((SolrCoreAware) inst).inform(core); ((SolrCoreAware) localInst).inform(core);
} }
if (inst instanceof ResourceLoaderAware) { if (localInst instanceof ResourceLoaderAware) {
SolrResourceLoader.assertAwareCompatibility(ResourceLoaderAware.class, inst); SolrResourceLoader.assertAwareCompatibility(ResourceLoaderAware.class, localInst);
try { try {
((ResourceLoaderAware) inst).inform(core.getResourceLoader()); ((ResourceLoaderAware) localInst).inform(core.getResourceLoader());
} catch (IOException e) { } catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error initializing component", e); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error initializing component", e);
} }
} }
lazyInst = localInst; // only assign the volatile until after the plugin is completely ready to use
return true;
} }