Remove ProxyClassLoader and PlatformProvider returning script classloader (#32806)
Closes #32804 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
ab3784adb0
commit
40049f31fa
6 changed files with 4 additions and 183 deletions
|
@ -26,7 +26,6 @@ import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
|
||||||
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
|
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
|
||||||
import org.hibernate.jpa.boot.spi.Bootstrap;
|
import org.hibernate.jpa.boot.spi.Bootstrap;
|
||||||
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
||||||
import org.keycloak.utils.ProxyClassLoader;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
|
@ -74,8 +73,7 @@ public class JpaUtils {
|
||||||
// to find and load the extra provided entities.
|
// to find and load the extra provided entities.
|
||||||
persistenceUnit.setTransactionType(txType);
|
persistenceUnit.setTransactionType(txType);
|
||||||
persistenceUnit.setValidationMode(ValidationMode.NONE.name());
|
persistenceUnit.setValidationMode(ValidationMode.NONE.name());
|
||||||
return Bootstrap.getEntityManagerFactoryBuilder(persistenceUnit, properties,
|
return Bootstrap.getEntityManagerFactoryBuilder(persistenceUnit, properties).build();
|
||||||
new ProxyClassLoader(providedEntities)).build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new RuntimeException("Persistence unit '" + unitName + "' not found");
|
throw new RuntimeException("Persistence unit '" + unitName + "' not found");
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.nio.file.Path;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.platform.PlatformProvider;
|
import org.keycloak.platform.PlatformProvider;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
|
|
||||||
|
@ -103,9 +102,4 @@ public class QuarkusPlatform implements PlatformProvider {
|
||||||
return tmpDir;
|
return tmpDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig) {
|
|
||||||
// It is fine to return null assuming that nashorn and it's dependencies are included on the classpath (usually "providers" directory)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed 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.keycloak.utils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:erik.mulder@docdatapayments.com">Erik Mulder</a>
|
|
||||||
*
|
|
||||||
* Classloader implementation to facilitate loading classes and resources from a collection of other classloaders.
|
|
||||||
* Effectively it forms a proxy to one or more other classloaders.
|
|
||||||
*
|
|
||||||
* The way it works:
|
|
||||||
* - Get list of classloaders, which will be used as "delegates" when loaded classes or resources.
|
|
||||||
* - Can be retrieved from provided classloaders or alternatively from the provided classes where the "delegate classloaders" will be determined from the classloaders of given classes
|
|
||||||
* - For each class or resource that is 'requested':
|
|
||||||
* - First try all provided classloaders and if we have a match, return that
|
|
||||||
* - If no match was found: proceed with 'normal' classloading in 'current classpath' scope
|
|
||||||
*
|
|
||||||
* In this particular context: only loadClass and getResource overrides are needed, since those
|
|
||||||
* are the methods that a classloading and resource loading process will need.
|
|
||||||
*/
|
|
||||||
public class ProxyClassLoader extends ClassLoader {
|
|
||||||
|
|
||||||
private Set<ClassLoader> classloaders;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init classloader with the list of given delegates
|
|
||||||
* @param delegateClassLoaders
|
|
||||||
*/
|
|
||||||
public ProxyClassLoader(ClassLoader... delegateClassLoaders) {
|
|
||||||
if (delegateClassLoaders == null || delegateClassLoaders.length == 0) {
|
|
||||||
throw new IllegalStateException("At least one classloader to delegate must be provided");
|
|
||||||
}
|
|
||||||
classloaders = new LinkedHashSet<>();
|
|
||||||
classloaders.addAll(Arrays.asList(delegateClassLoaders));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all unique classloaders from the provided classes to be used as "Delegate classloaders"
|
|
||||||
* @param classes
|
|
||||||
*/
|
|
||||||
public ProxyClassLoader(Collection<Class<?>> classes) {
|
|
||||||
init(classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Collection<Class<?>> classes) {
|
|
||||||
classloaders = new LinkedHashSet<>();
|
|
||||||
for (Class<?> clazz : classes) {
|
|
||||||
classloaders.add(clazz.getClassLoader());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
|
||||||
for (ClassLoader classloader : classloaders) {
|
|
||||||
try {
|
|
||||||
return classloader.loadClass(name);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// This particular class loader did not find the class. It's expected behavior that
|
|
||||||
// this can happen, so we'll just ignore the exception and let the next one try.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We did not find the class in the proxy class loaders, so proceed with 'normal' behavior.
|
|
||||||
return super.loadClass(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URL getResource(String name) {
|
|
||||||
for (ClassLoader classloader : classloaders) {
|
|
||||||
URL resource = classloader.getResource(name);
|
|
||||||
if (resource != null) {
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
// Resource == null means not found, so let the next one try.
|
|
||||||
}
|
|
||||||
// We could not get the resource from the proxy class loaders, so proceed with 'normal' behavior.
|
|
||||||
return super.getResource(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<URL> getResources(String name) throws IOException {
|
|
||||||
final LinkedHashSet<URL> resourceUrls = new LinkedHashSet();
|
|
||||||
|
|
||||||
for (ClassLoader classloader : classloaders) {
|
|
||||||
Enumeration<URL> child = classloader.getResources(name);
|
|
||||||
|
|
||||||
while (child.hasMoreElements()) {
|
|
||||||
resourceUrls.add(child.nextElement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Enumeration<URL>() {
|
|
||||||
final Iterator<URL> resourceUrlIterator = resourceUrls.iterator();
|
|
||||||
|
|
||||||
public boolean hasMoreElements() {
|
|
||||||
return this.resourceUrlIterator.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public URL nextElement() {
|
|
||||||
return (URL)this.resourceUrlIterator.next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ProxyClassLoader: Delegates: " + classloaders;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -41,17 +41,4 @@ public interface PlatformProvider {
|
||||||
*/
|
*/
|
||||||
File getTmpDirectory();
|
File getTmpDirectory();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns classloader to load script engine. Classloader should contain the implementation of {@link javax.script.ScriptEngineFactory}
|
|
||||||
* and it's definition inside META-INF/services of the jar file(s), which will be provided by this classloader.
|
|
||||||
*
|
|
||||||
* This method can return null and in that case, the default Keycloak services classloader will be used for load script engine. Note that java versions earlier than 15 always contain
|
|
||||||
* the "nashorn" script engine by default on the classpath (it is part of the Java platform itself) and hence for them it is always fine to return null (unless you want to override default engine)
|
|
||||||
*
|
|
||||||
* @param scriptProviderConfig Configuration scope of the "default" provider of "scripting" SPI. It can contain some config properties for the classloader (EG. file path)
|
|
||||||
* @return classloader or null
|
|
||||||
*/
|
|
||||||
ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.ScriptModel;
|
import org.keycloak.models.ScriptModel;
|
||||||
import org.keycloak.platform.Platform;
|
import org.keycloak.platform.Platform;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
import org.keycloak.utils.ProxyClassLoader;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ScriptingProvider} that uses a {@link ScriptEngineManager} to evaluate scripts with a {@link ScriptEngine}.
|
* A {@link ScriptingProvider} that uses a {@link ScriptEngineManager} to evaluate scripts with a {@link ScriptEngine}.
|
||||||
|
@ -128,24 +127,6 @@ public class DefaultScriptingProvider implements ScriptingProvider {
|
||||||
* Looks-up a {@link ScriptEngine} based on the MIME-type provided by the given {@link Script}.
|
* Looks-up a {@link ScriptEngine} based on the MIME-type provided by the given {@link Script}.
|
||||||
*/
|
*/
|
||||||
private ScriptEngine lookupScriptEngineFor(ScriptModel script) {
|
private ScriptEngine lookupScriptEngineFor(ScriptModel script) {
|
||||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
return new ScriptEngineManager().getEngineByMimeType(script.getMimeType());
|
||||||
try {
|
|
||||||
ClassLoader scriptClassLoader = Platform.getPlatform().getScriptEngineClassLoader(factory.getConfig());
|
|
||||||
|
|
||||||
// Also need to use classloader of keycloak services itself to be able to use keycloak classes in the scripts
|
|
||||||
if (scriptClassLoader != null) {
|
|
||||||
scriptClassLoader = new ProxyClassLoader(scriptClassLoader, DefaultScriptingProvider.class.getClassLoader());
|
|
||||||
} else {
|
|
||||||
scriptClassLoader = DefaultScriptingProvider.class.getClassLoader();
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debugf("Using classloader %s to load script engine", scriptClassLoader);
|
|
||||||
|
|
||||||
Thread.currentThread().setContextClassLoader(scriptClassLoader);
|
|
||||||
return new ScriptEngineManager().getEngineByMimeType(script.getMimeType());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.profile.PropertiesFileProfileConfigResolver;
|
import org.keycloak.common.profile.PropertiesFileProfileConfigResolver;
|
||||||
import org.keycloak.common.profile.PropertiesProfileConfigResolver;
|
import org.keycloak.common.profile.PropertiesProfileConfigResolver;
|
||||||
|
@ -87,9 +86,4 @@ public class TestPlatform implements PlatformProvider {
|
||||||
return tmpDir;
|
return tmpDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig) {
|
|
||||||
// It is fine to return null as nashorn should be automatically included on the classpath of testsuite utils
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue