Support for script providers when running in embedded mode
Closes #27574 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
ba7e27a105
commit
d5a613cd6b
2 changed files with 89 additions and 33 deletions
|
@ -126,6 +126,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -694,12 +695,12 @@ class KeycloakProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, ProviderFactory> deployedScriptProviders = loadDeployedScriptProviders(classLoader, spi);
|
Map<String, ProviderFactory<?>> deployedScriptProviders = loadDeployedScriptProviders(classLoader, spi);
|
||||||
|
|
||||||
loadedFactories.addAll(deployedScriptProviders.values());
|
loadedFactories.addAll(deployedScriptProviders.values());
|
||||||
preConfiguredProviders.putAll(deployedScriptProviders);
|
preConfiguredProviders.putAll(deployedScriptProviders);
|
||||||
|
|
||||||
for (ProviderFactory factory : loadedFactories) {
|
for (ProviderFactory<?> factory : loadedFactories) {
|
||||||
if (IGNORED_PROVIDER_FACTORY.contains(factory.getClass())) {
|
if (IGNORED_PROVIDER_FACTORY.contains(factory.getClass())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -724,31 +725,29 @@ class KeycloakProcessor {
|
||||||
return factories;
|
return factories;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ProviderFactory> loadDeployedScriptProviders(ClassLoader classLoader, Spi spi) {
|
private Map<String, ProviderFactory<?>> loadDeployedScriptProviders(ClassLoader classLoader, Spi spi) {
|
||||||
Map<String, ProviderFactory> providers = new HashMap<>();
|
Map<String, ProviderFactory<?>> providers = new HashMap<>();
|
||||||
|
|
||||||
if (supportsDeployeableScripts(spi)) {
|
if (supportsDeployeableScripts(spi)) {
|
||||||
try {
|
try {
|
||||||
Enumeration<URL> urls = classLoader.getResources(KEYCLOAK_SCRIPTS_JSON_PATH);
|
Enumeration<URL> descriptorsUrls = classLoader.getResources(KEYCLOAK_SCRIPTS_JSON_PATH);
|
||||||
|
|
||||||
while (urls.hasMoreElements()) {
|
while (descriptorsUrls.hasMoreElements()) {
|
||||||
URL url = urls.nextElement();
|
URL url = descriptorsUrls.nextElement();
|
||||||
int fileSeparator = url.getFile().indexOf(JAR_FILE_SEPARATOR);
|
List<ScriptProviderDescriptor> descriptors = getScriptProviderDescriptorsFromJarFile(url);
|
||||||
|
|
||||||
if (fileSeparator != -1) {
|
if (!Environment.isDistribution()) {
|
||||||
JarFile jarFile = new JarFile(url.getFile().substring("file:".length(), fileSeparator));
|
// script providers are only loaded from classpath when running embedded
|
||||||
JarEntry descriptorEntry = jarFile.getJarEntry(KEYCLOAK_SCRIPTS_JSON_PATH);
|
descriptors = new ArrayList<>(descriptors);
|
||||||
ScriptProviderDescriptor descriptor;
|
descriptors.addAll(getScriptProviderDescriptorsFromClassPath(url));
|
||||||
|
}
|
||||||
try (InputStream is = jarFile.getInputStream(descriptorEntry)) {
|
|
||||||
descriptor = JsonSerialization.readValue(is, ScriptProviderDescriptor.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (ScriptProviderDescriptor descriptor : descriptors) {
|
||||||
for (Entry<String, List<ScriptProviderMetadata>> entry : descriptor.getProviders().entrySet()) {
|
for (Entry<String, List<ScriptProviderMetadata>> entry : descriptor.getProviders().entrySet()) {
|
||||||
if (isScriptForSpi(spi, entry.getKey())) {
|
if (isScriptForSpi(spi, entry.getKey())) {
|
||||||
for (ScriptProviderMetadata metadata : entry.getValue()) {
|
for (ScriptProviderMetadata metadata : entry.getValue()) {
|
||||||
ProviderFactory provider = createDeployableScriptProvider(jarFile, entry, metadata);
|
ProviderFactory<?> factory = DEPLOYEABLE_SCRIPT_PROVIDERS.get(entry.getKey()).apply(metadata);
|
||||||
providers.put(metadata.getId(), provider);
|
providers.put(metadata.getId(), factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,31 +761,89 @@ class KeycloakProcessor {
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProviderFactory createDeployableScriptProvider(JarFile jarFile, Entry<String, List<ScriptProviderMetadata>> entry,
|
private List<ScriptProviderDescriptor> getScriptProviderDescriptorsFromClassPath(URL url) throws IOException {
|
||||||
ScriptProviderMetadata metadata) throws IOException {
|
String file = url.getFile();
|
||||||
String fileName = metadata.getFileName();
|
|
||||||
|
|
||||||
if (fileName == null) {
|
if (!file.endsWith(".json")) {
|
||||||
throw new RuntimeException("You must provide the script file name");
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
JarEntry scriptFile = jarFile.getJarEntry(fileName);
|
List<ScriptProviderDescriptor> descriptors = new ArrayList<>();
|
||||||
|
|
||||||
try (InputStream in = jarFile.getInputStream(scriptFile)) {
|
try (InputStream is = url.openStream()) {
|
||||||
metadata.setCode(StreamUtil.readString(in, StandardCharsets.UTF_8));
|
ScriptProviderDescriptor descriptor = JsonSerialization.readValue(is, ScriptProviderDescriptor.class);
|
||||||
|
|
||||||
|
configureScriptDescriptor(descriptor, fileName -> {
|
||||||
|
// descriptor is at META-INF/
|
||||||
|
Path basePath = Path.of(url.getPath()).getParent().getParent();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return basePath.resolve(fileName).toUri().toURL().openStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to read script file from: " + fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
descriptors.add(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.setId(new StringBuilder("script").append("-").append(fileName).toString());
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
String name = metadata.getName();
|
private List<ScriptProviderDescriptor> getScriptProviderDescriptorsFromJarFile(URL url) throws IOException {
|
||||||
|
String file = url.getFile();
|
||||||
|
|
||||||
if (name == null) {
|
if (!file.contains(JAR_FILE_SEPARATOR)) {
|
||||||
name = fileName;
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.setName(name);
|
List<ScriptProviderDescriptor> descriptors = new ArrayList<>();
|
||||||
|
|
||||||
return DEPLOYEABLE_SCRIPT_PROVIDERS.get(entry.getKey()).apply(metadata);
|
try (JarFile jarFile = new JarFile(file.substring("file:".length(), file.indexOf(JAR_FILE_SEPARATOR)))) {
|
||||||
|
JarEntry descriptorEntry = jarFile.getJarEntry(KEYCLOAK_SCRIPTS_JSON_PATH);
|
||||||
|
|
||||||
|
try (InputStream is = jarFile.getInputStream(descriptorEntry)) {
|
||||||
|
ScriptProviderDescriptor descriptor = JsonSerialization.readValue(is, ScriptProviderDescriptor.class);
|
||||||
|
|
||||||
|
configureScriptDescriptor(descriptor, fileName -> {
|
||||||
|
try {
|
||||||
|
JarEntry scriptFile = jarFile.getJarEntry(fileName);
|
||||||
|
return jarFile.getInputStream(scriptFile);
|
||||||
|
} catch (IOException cause) {
|
||||||
|
throw new RuntimeException("Failed to read script file from file: " + fileName, cause);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
descriptors.add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void configureScriptDescriptor(ScriptProviderDescriptor descriptor, Function<String, InputStream> jsFileLoader) throws IOException {
|
||||||
|
for (List<ScriptProviderMetadata> metadatas : descriptor.getProviders().values()) {
|
||||||
|
for (ScriptProviderMetadata metadata : metadatas) {
|
||||||
|
String fileName = metadata.getFileName();
|
||||||
|
|
||||||
|
if (fileName == null) {
|
||||||
|
throw new RuntimeException("You must provide the script file name");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream in = jsFileLoader.apply(fileName)) {
|
||||||
|
metadata.setCode(StreamUtil.readString(in, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata.setId(new StringBuilder("script").append("-").append(fileName).toString());
|
||||||
|
|
||||||
|
String name = metadata.getName();
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
name = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata.setName(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isScriptForSpi(Spi spi, String type) {
|
private boolean isScriptForSpi(Spi spi, String type) {
|
||||||
|
|
|
@ -58,7 +58,6 @@ When running in embedded mode, the `build` phase happens every time the server i
|
||||||
There are a few limitations when running tests. The well-known limitations are:
|
There are a few limitations when running tests. The well-known limitations are:
|
||||||
|
|
||||||
* FIPS tests not working
|
* FIPS tests not working
|
||||||
* Deploying script providers not working. Probably any test deploying JAR files.
|
|
||||||
* Re-starting the server during a test execution is taking too much metaspace. Need more investigation.
|
* Re-starting the server during a test execution is taking too much metaspace. Need more investigation.
|
||||||
|
|
||||||
## Debugging - tips & tricks
|
## Debugging - tips & tricks
|
||||||
|
|
Loading…
Reference in a new issue