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.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
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());
|
||||
preConfiguredProviders.putAll(deployedScriptProviders);
|
||||
|
||||
for (ProviderFactory factory : loadedFactories) {
|
||||
for (ProviderFactory<?> factory : loadedFactories) {
|
||||
if (IGNORED_PROVIDER_FACTORY.contains(factory.getClass())) {
|
||||
continue;
|
||||
}
|
||||
|
@ -724,31 +725,29 @@ class KeycloakProcessor {
|
|||
return factories;
|
||||
}
|
||||
|
||||
private Map<String, ProviderFactory> loadDeployedScriptProviders(ClassLoader classLoader, Spi spi) {
|
||||
Map<String, ProviderFactory> providers = new HashMap<>();
|
||||
private Map<String, ProviderFactory<?>> loadDeployedScriptProviders(ClassLoader classLoader, Spi spi) {
|
||||
Map<String, ProviderFactory<?>> providers = new HashMap<>();
|
||||
|
||||
if (supportsDeployeableScripts(spi)) {
|
||||
try {
|
||||
Enumeration<URL> urls = classLoader.getResources(KEYCLOAK_SCRIPTS_JSON_PATH);
|
||||
Enumeration<URL> descriptorsUrls = classLoader.getResources(KEYCLOAK_SCRIPTS_JSON_PATH);
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
int fileSeparator = url.getFile().indexOf(JAR_FILE_SEPARATOR);
|
||||
while (descriptorsUrls.hasMoreElements()) {
|
||||
URL url = descriptorsUrls.nextElement();
|
||||
List<ScriptProviderDescriptor> descriptors = getScriptProviderDescriptorsFromJarFile(url);
|
||||
|
||||
if (fileSeparator != -1) {
|
||||
JarFile jarFile = new JarFile(url.getFile().substring("file:".length(), fileSeparator));
|
||||
JarEntry descriptorEntry = jarFile.getJarEntry(KEYCLOAK_SCRIPTS_JSON_PATH);
|
||||
ScriptProviderDescriptor descriptor;
|
||||
|
||||
try (InputStream is = jarFile.getInputStream(descriptorEntry)) {
|
||||
descriptor = JsonSerialization.readValue(is, ScriptProviderDescriptor.class);
|
||||
}
|
||||
if (!Environment.isDistribution()) {
|
||||
// script providers are only loaded from classpath when running embedded
|
||||
descriptors = new ArrayList<>(descriptors);
|
||||
descriptors.addAll(getScriptProviderDescriptorsFromClassPath(url));
|
||||
}
|
||||
|
||||
for (ScriptProviderDescriptor descriptor : descriptors) {
|
||||
for (Entry<String, List<ScriptProviderMetadata>> entry : descriptor.getProviders().entrySet()) {
|
||||
if (isScriptForSpi(spi, entry.getKey())) {
|
||||
for (ScriptProviderMetadata metadata : entry.getValue()) {
|
||||
ProviderFactory provider = createDeployableScriptProvider(jarFile, entry, metadata);
|
||||
providers.put(metadata.getId(), provider);
|
||||
ProviderFactory<?> factory = DEPLOYEABLE_SCRIPT_PROVIDERS.get(entry.getKey()).apply(metadata);
|
||||
providers.put(metadata.getId(), factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -762,31 +761,89 @@ class KeycloakProcessor {
|
|||
return providers;
|
||||
}
|
||||
|
||||
private ProviderFactory createDeployableScriptProvider(JarFile jarFile, Entry<String, List<ScriptProviderMetadata>> entry,
|
||||
ScriptProviderMetadata metadata) throws IOException {
|
||||
String fileName = metadata.getFileName();
|
||||
private List<ScriptProviderDescriptor> getScriptProviderDescriptorsFromClassPath(URL url) throws IOException {
|
||||
String file = url.getFile();
|
||||
|
||||
if (fileName == null) {
|
||||
throw new RuntimeException("You must provide the script file name");
|
||||
if (!file.endsWith(".json")) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
JarEntry scriptFile = jarFile.getJarEntry(fileName);
|
||||
List<ScriptProviderDescriptor> descriptors = new ArrayList<>();
|
||||
|
||||
try (InputStream in = jarFile.getInputStream(scriptFile)) {
|
||||
metadata.setCode(StreamUtil.readString(in, StandardCharsets.UTF_8));
|
||||
try (InputStream is = url.openStream()) {
|
||||
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) {
|
||||
name = fileName;
|
||||
if (!file.contains(JAR_FILE_SEPARATOR)) {
|
||||
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) {
|
||||
|
|
|
@ -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:
|
||||
|
||||
* 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.
|
||||
|
||||
## Debugging - tips & tricks
|
||||
|
|
Loading…
Reference in a new issue