blob: 8058f214c6b81e77ac1ce693e2f259321b692b99 [file] [log] [blame]
/*
* 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 java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.util.StrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MemClassLoader extends ClassLoader implements AutoCloseable, ResourceLoader {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private boolean allJarsLoaded = false;
private final SolrResourceLoader parentLoader;
private List<PluginBag.RuntimeLib> libs = new ArrayList<>();
@SuppressWarnings("rawtypes")
private Map<String, Class> classCache = new HashMap<>();
private List<String> errors = new ArrayList<>();
public MemClassLoader(List<PluginBag.RuntimeLib> libs, SolrResourceLoader resourceLoader) {
this.parentLoader = resourceLoader;
this.libs = libs;
}
synchronized void loadRemoteJars() {
if (allJarsLoaded) return;
int count = 0;
for (PluginBag.RuntimeLib lib : libs) {
if (lib.getUrl() != null) {
try {
lib.loadJar();
lib.verify();
} catch (Exception e) {
log.error("Error loading runtime library", e);
}
count++;
}
}
if (count == libs.size()) allJarsLoaded = true;
}
public synchronized void loadJars() {
if (allJarsLoaded) return;
for (PluginBag.RuntimeLib lib : libs) {
try {
lib.loadJar();
lib.verify();
} catch (Exception exception) {
errors.add(exception.getMessage());
if (exception instanceof SolrException) throw (SolrException) exception;
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Atleast one runtimeLib could not be loaded", exception);
}
}
allJarsLoaded = true;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if(!allJarsLoaded ) loadJars();
try {
return parentLoader.findClass(name, Object.class);
} catch (Exception e) {
return loadFromRuntimeLibs(name);
}
}
@SuppressWarnings({"rawtypes"})
private synchronized Class<?> loadFromRuntimeLibs(String name) throws ClassNotFoundException {
Class result = classCache.get(name);
if(result != null)
return result;
AtomicReference<String> jarName = new AtomicReference<>();
ByteBuffer buf = null;
try {
buf = getByteBuffer(name, jarName);
} catch (Exception e) {
throw new ClassNotFoundException("class could not be loaded " + name + (errors.isEmpty()? "": "Some dynamic libraries could not be loaded: "+ StrUtils.join(errors, '|')), e);
}
if (buf == null) throw new ClassNotFoundException("Class not found :" + name);
ProtectionDomain defaultDomain = null;
//using the default protection domain, with no permissions
try {
defaultDomain = new ProtectionDomain(new CodeSource(new URL("http://localhost/" + CollectionAdminParams.SYSTEM_COLL + "/blob/" + jarName.get()), (Certificate[]) null),
null);
} catch (MalformedURLException mue) {
throw new ClassNotFoundException("Unexpected exception ", mue);
//should not happen
}
log.info("Defining_class {} from runtime jar {} ", name, jarName);
result = defineClass(name, buf.array(), buf.arrayOffset(), buf.limit(), defaultDomain);
classCache.put(name, result);
return result;
}
private ByteBuffer getByteBuffer(String name, AtomicReference<String> jarName) throws Exception {
if (!allJarsLoaded) {
loadJars();
}
String path = name.replace('.', '/').concat(".class");
ByteBuffer buf = null;
for (PluginBag.RuntimeLib lib : libs) {
try {
buf = lib.getFileContent(path);
if (buf != null) {
jarName.set(lib.getName());
break;
}
} catch (Exception exp) {
throw new ClassNotFoundException("Unable to load class :" + name, exp);
}
}
return buf;
}
@Override
public void close() {
for (PluginBag.RuntimeLib lib : libs) {
try {
lib.close();
} catch (Exception e) {
log.error("Error closing lib {}", lib.getName(), e);
}
}
}
@Override
public InputStream openResource(String resource) throws IOException {
AtomicReference<String> jarName = new AtomicReference<>();
try {
ByteBuffer buf = getByteBuffer(resource, jarName);
if (buf == null) throw new IOException("Resource could not be found " + resource);
} catch (Exception e) {
throw new IOException("Resource could not be found " + resource, e);
}
return null;
}
@Override
public <T> Class<? extends T> findClass(String cname, Class<T> expectedType) {
if(!allJarsLoaded ) loadJars();
try {
return findClass(cname).asSubclass(expectedType);
} catch (Exception e) {
if (e instanceof SolrException) {
throw (SolrException) e;
} else {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error loading class " + cname, e);
}
}
}
@Override
public <T> T newInstance(String cname, Class<T> expectedType) {
try {
return findClass(cname, expectedType).newInstance();
} catch (SolrException e) {
throw e;
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error instantiating class :" + cname, e);
}
}
}