diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 31ed45f3d9..249cc25ef5 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -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 deployedScriptProviders = loadDeployedScriptProviders(classLoader, spi); + Map> 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 loadDeployedScriptProviders(ClassLoader classLoader, Spi spi) { - Map providers = new HashMap<>(); + private Map> loadDeployedScriptProviders(ClassLoader classLoader, Spi spi) { + Map> providers = new HashMap<>(); if (supportsDeployeableScripts(spi)) { try { - Enumeration urls = classLoader.getResources(KEYCLOAK_SCRIPTS_JSON_PATH); + Enumeration 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 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> 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> entry, - ScriptProviderMetadata metadata) throws IOException { - String fileName = metadata.getFileName(); + private List 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 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 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 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 jsFileLoader) throws IOException { + for (List 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) { diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md index 073be96a32..a60d130c81 100644 --- a/testsuite/integration-arquillian/HOW-TO-RUN.md +++ b/testsuite/integration-arquillian/HOW-TO-RUN.md @@ -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