KEYCLOAK-14548 Add support for cached gzip encoding of resources
This commit is contained in:
parent
e34ff6cd9c
commit
76f7fbb984
15 changed files with 376 additions and 23 deletions
|
@ -68,6 +68,7 @@
|
|||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="javax.json.api"/>
|
||||
<module name="org.apache.commons.io"/>
|
||||
<module name="org.apache.httpcomponents"/>
|
||||
<module name="org.twitter4j"/>
|
||||
<module name="javax.transaction.api"/>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package org.keycloak.encoding;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class GzipResourceEncodingProvider implements ResourceEncodingProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ResourceEncodingProvider.class);
|
||||
|
||||
private KeycloakSession session;
|
||||
private File cacheDir;
|
||||
|
||||
public GzipResourceEncodingProvider(KeycloakSession session, File cacheDir) {
|
||||
this.session = session;
|
||||
this.cacheDir = cacheDir;
|
||||
}
|
||||
|
||||
public InputStream getEncodedStream(StreamSupplier producer, String... path) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(cacheDir.getAbsolutePath());
|
||||
for (String p : path) {
|
||||
sb.append(File.separatorChar);
|
||||
sb.append(p);
|
||||
}
|
||||
sb.append(".gz");
|
||||
|
||||
String filePath = sb.toString();
|
||||
|
||||
try {
|
||||
File encodedFile = new File(filePath);
|
||||
if (!encodedFile.getCanonicalPath().startsWith(cacheDir.getCanonicalPath())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!encodedFile.exists()) {
|
||||
InputStream is = producer.getInputStream();
|
||||
if (is != null) {
|
||||
File parent = encodedFile.getParentFile();
|
||||
if (!parent.isDirectory()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(encodedFile);
|
||||
GZIPOutputStream gos = new GZIPOutputStream(fos);
|
||||
IOUtils.copy(is, gos);
|
||||
gos.close();
|
||||
is.close();
|
||||
} else {
|
||||
encodedFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
return encodedFile != null ? new FileInputStream(encodedFile) : null;
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to encode resource", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return "gzip";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package org.keycloak.encoding;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class GzipResourceEncodingProviderFactory implements ResourceEncodingProviderFactory {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(GzipResourceEncodingProviderFactory.class);
|
||||
|
||||
private Set<String> excludedContentTypes = new HashSet<>();
|
||||
|
||||
private File cacheDir;
|
||||
|
||||
@Override
|
||||
public ResourceEncodingProvider create(KeycloakSession session) {
|
||||
if (cacheDir == null) {
|
||||
cacheDir = initCacheDir();
|
||||
}
|
||||
|
||||
return new GzipResourceEncodingProvider(session, cacheDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String e = config.get("excludedContentTypes", "image/png image/jpeg");
|
||||
for (String s : e.split(" ")) {
|
||||
excludedContentTypes.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean encodeContentType(String contentType) {
|
||||
return !excludedContentTypes.contains(contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "gzip";
|
||||
}
|
||||
|
||||
private synchronized File initCacheDir() {
|
||||
if (cacheDir != null) {
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
File cacheRoot = new File(System.getProperty("java.io.tmpdir"), "kc-gzip-cache");
|
||||
File cacheDir = new File(cacheRoot, Version.RESOURCES_VERSION);
|
||||
|
||||
if (cacheRoot.isDirectory()) {
|
||||
for (File f : cacheRoot.listFiles()) {
|
||||
if (!f.getName().equals(Version.RESOURCES_VERSION)) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(f);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to delete old gzip cache directory", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cacheDir.isDirectory() && !cacheDir.mkdirs()) {
|
||||
logger.warn("Failed to create gzip cache directory");
|
||||
return null;
|
||||
}
|
||||
|
||||
return cacheDir;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.encoding;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
public class ResourceEncodingHelper {
|
||||
|
||||
public static ResourceEncodingProvider getResourceEncodingProvider(KeycloakSession session, String contentType) {
|
||||
String acceptEncoding = session.getContext().getRequestHeaders().getHeaderString("Accept-Encoding");
|
||||
if (acceptEncoding != null) {
|
||||
for (String e : acceptEncoding.split(",")) {
|
||||
e = e.trim();
|
||||
ResourceEncodingProviderFactory f = (ResourceEncodingProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(ResourceEncodingProvider.class, e);
|
||||
if (f != null && f.encodeContentType(contentType)) {
|
||||
ResourceEncodingProvider provider = session.getProvider(ResourceEncodingProvider.class, e.trim());
|
||||
if (provider != null) {
|
||||
return provider;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.keycloak.encoding;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface ResourceEncodingProvider extends Provider {
|
||||
|
||||
InputStream getEncodedStream(StreamSupplier producer, String... path);
|
||||
|
||||
String getEncoding();
|
||||
|
||||
@Override
|
||||
default void close() {
|
||||
}
|
||||
|
||||
interface StreamSupplier {
|
||||
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.keycloak.encoding;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
public interface ResourceEncodingProviderFactory extends ProviderFactory<ResourceEncodingProvider> {
|
||||
|
||||
boolean encodeContentType(String contentType);
|
||||
|
||||
@Override
|
||||
default void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void close() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.keycloak.encoding;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
public class ResourceEncodingSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "resource-encoding";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return ResourceEncodingProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return ResourceEncodingProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,9 @@ package org.keycloak.services.resources;
|
|||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.encoding.ResourceEncodingHelper;
|
||||
import org.keycloak.encoding.ResourceEncodingProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.utils.MediaType;
|
||||
|
||||
|
@ -40,6 +43,9 @@ import java.io.InputStream;
|
|||
@Path("/js")
|
||||
public class JsResource {
|
||||
|
||||
@Context
|
||||
private KeycloakSession session;
|
||||
|
||||
@Context
|
||||
private HttpRequest request;
|
||||
|
||||
|
@ -120,11 +126,24 @@ public class JsResource {
|
|||
cacheControl = CacheControlUtil.noCache();
|
||||
}
|
||||
|
||||
String contentType = "text/javascript";
|
||||
Cors cors = Cors.add(request).allowAllOrigins();
|
||||
|
||||
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name);
|
||||
ResourceEncodingProvider encodingProvider = ResourceEncodingHelper.getResourceEncodingProvider(session, contentType);
|
||||
|
||||
InputStream inputStream;
|
||||
if (encodingProvider != null) {
|
||||
inputStream = encodingProvider.getEncodedStream(() -> getClass().getClassLoader().getResourceAsStream(name), "js", name);
|
||||
} else {
|
||||
inputStream = getClass().getClassLoader().getResourceAsStream(name);
|
||||
}
|
||||
|
||||
if (inputStream != null) {
|
||||
return cors.builder(Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl)).build();
|
||||
Response.ResponseBuilder rb = Response.ok(inputStream).type(contentType).cacheControl(cacheControl);
|
||||
if (encodingProvider != null) {
|
||||
rb.encoding(encodingProvider.getEncoding());
|
||||
}
|
||||
return cors.builder(rb).build();
|
||||
} else {
|
||||
return cors.builder(Response.status(Response.Status.NOT_FOUND)).build();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.services.resources;
|
|||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.config.ConfigProviderFactory;
|
||||
|
@ -160,7 +161,6 @@ public class KeycloakApplication extends Application {
|
|||
sessionFactory.publish(new PostMigrationEvent());
|
||||
|
||||
setupScheduledTasks(sessionFactory);
|
||||
|
||||
}
|
||||
|
||||
protected void shutdown() {
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.keycloak.services.resources;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.common.util.MimeTypeUtil;
|
||||
import org.keycloak.encoding.ResourceEncodingHelper;
|
||||
import org.keycloak.encoding.ResourceEncodingProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
|
@ -29,6 +31,7 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
|
@ -39,8 +42,6 @@ import java.io.InputStream;
|
|||
@Path("/resources")
|
||||
public class ThemeResource {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(ThemeResource.class);
|
||||
|
||||
@Context
|
||||
private KeycloakSession session;
|
||||
|
||||
|
@ -60,10 +61,23 @@ public class ThemeResource {
|
|||
}
|
||||
|
||||
try {
|
||||
String contentType = MimeTypeUtil.getContentType(path);
|
||||
Theme theme = session.theme().getTheme(themeName, Theme.Type.valueOf(themType.toUpperCase()));
|
||||
InputStream resource = theme.getResourceAsStream(path);
|
||||
ResourceEncodingProvider encodingProvider = ResourceEncodingHelper.getResourceEncodingProvider(session, contentType);
|
||||
|
||||
InputStream resource;
|
||||
if (encodingProvider != null) {
|
||||
resource = encodingProvider.getEncodedStream(() -> theme.getResourceAsStream(path), themType, themeName, path.replace('/', File.separatorChar));
|
||||
} else {
|
||||
resource = theme.getResourceAsStream(path);
|
||||
}
|
||||
|
||||
if (resource != null) {
|
||||
return Response.ok(resource).type(MimeTypeUtil.getContentType(path)).cacheControl(CacheControlUtil.getDefaultCacheControl()).build();
|
||||
Response.ResponseBuilder rb = Response.ok(resource).type(contentType).cacheControl(CacheControlUtil.getDefaultCacheControl());
|
||||
if (encodingProvider != null) {
|
||||
rb.encoding(encodingProvider.getEncoding());
|
||||
}
|
||||
return rb.build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.encoding.GzipResourceEncodingProviderFactory
|
|
@ -22,4 +22,5 @@ org.keycloak.services.clientregistration.policy.ClientRegistrationPolicySpi
|
|||
org.keycloak.authentication.actiontoken.ActionTokenHandlerSpi
|
||||
org.keycloak.services.x509.X509ClientCertificateLookupSpi
|
||||
org.keycloak.protocol.oidc.ext.OIDCExtSPI
|
||||
org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessorSpi
|
||||
org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessorSpi
|
||||
org.keycloak.encoding.ResourceEncodingSpi
|
|
@ -39,6 +39,7 @@ import org.jboss.logging.Logger;
|
|||
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
|
||||
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
|
||||
import org.jboss.resteasy.spi.ResteasyDeployment;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.jboss.shrinkwrap.api.Archive;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
|
||||
|
@ -83,6 +84,9 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
|
|||
// RESTEASY-2034
|
||||
deployment.setProperty(ResteasyContextParameters.RESTEASY_DISABLE_HTML_SANITIZER, true);
|
||||
|
||||
// Prevent double gzip encoding of resources
|
||||
deployment.getDisabledProviderClasses().add("org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor");
|
||||
|
||||
DeploymentInfo di = undertow.undertowDeployment(deployment);
|
||||
di.setClassLoader(getClass().getClassLoader());
|
||||
di.setContextPath("/auth");
|
||||
|
|
|
@ -1,15 +1,37 @@
|
|||
package org.keycloak.testsuite.theme;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||
import org.keycloak.testsuite.utils.io.IOUtil;
|
||||
import org.keycloak.theme.Theme;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@AuthServerContainerExclude(AuthServer.REMOTE)
|
||||
public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
||||
|
@ -56,6 +78,48 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gzipEncoding() throws IOException {
|
||||
final String resourcesVersion = testingClient.server().fetch(session -> Version.RESOURCES_VERSION, String.class);
|
||||
|
||||
assertEncoded(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/resources/" + resourcesVersion + "/welcome/keycloak/css/welcome.css", "body {");
|
||||
assertEncoded(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/js/keycloak.js", "function(root, factory)");
|
||||
|
||||
testingClient.server().run(session -> {
|
||||
assertTrue(Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "welcome", "keycloak", "css", "welcome.css.gz").toFile().isFile());
|
||||
assertTrue(Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "js", "keycloak.js.gz").toFile().isFile());
|
||||
});
|
||||
}
|
||||
|
||||
private void assertEncoded(String url, String expectedContent) throws IOException {
|
||||
try (CloseableHttpClient httpClient = HttpClientBuilder.create().disableContentCompression().build()) {
|
||||
HttpGet get = new HttpGet(url);
|
||||
CloseableHttpResponse response = httpClient.execute(get);
|
||||
|
||||
InputStream is = response.getEntity().getContent();
|
||||
assertNull(response.getFirstHeader("Content-Encoding"));
|
||||
|
||||
String plain = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||
|
||||
response.close();
|
||||
|
||||
get = new HttpGet(url);
|
||||
get.addHeader("Accept-Encoding", "gzip");
|
||||
response = httpClient.execute(get);
|
||||
|
||||
|
||||
is = response.getEntity().getContent();
|
||||
assertEquals("gzip", response.getFirstHeader("Content-Encoding").getValue());
|
||||
|
||||
String gzip = IOUtils.toString(new GZIPInputStream(is), StandardCharsets.UTF_8);
|
||||
|
||||
response.close();
|
||||
|
||||
assertEquals(plain, gzip);
|
||||
assertTrue(plain.contains(expectedContent));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See KEYCLOAK-12926
|
||||
*/
|
||||
|
@ -64,25 +128,25 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
|||
testingClient.server().run(session -> {
|
||||
try {
|
||||
Theme theme = session.theme().getTheme("base", Theme.Type.LOGIN);
|
||||
Assert.assertEquals("Test en_US_variant", theme.getMessages("messages", new Locale("en", "US", "variant")).get("test.keycloak-12926"));
|
||||
Assert.assertEquals("Test en_US", theme.getMessages("messages", new Locale("en", "US")).get("test.keycloak-12926"));
|
||||
Assert.assertEquals("Test en", theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926"));
|
||||
Assert.assertEquals("Test en_US", theme.getMessages("messages", new Locale("en", "US")).get("test.keycloak-12926"));
|
||||
Assert.assertEquals("Test en", theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926"));
|
||||
assertEquals("Test en_US_variant", theme.getMessages("messages", new Locale("en", "US", "variant")).get("test.keycloak-12926"));
|
||||
assertEquals("Test en_US", theme.getMessages("messages", new Locale("en", "US")).get("test.keycloak-12926"));
|
||||
assertEquals("Test en", theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926"));
|
||||
assertEquals("Test en_US", theme.getMessages("messages", new Locale("en", "US")).get("test.keycloak-12926"));
|
||||
assertEquals("Test en", theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926"));
|
||||
|
||||
Assert.assertEquals("only de_AT_variant", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-resolving1"));
|
||||
Assert.assertNull(theme.getMessages("messages", new Locale("de", "AT")).get("test.keycloak-12926-resolving1"));
|
||||
assertEquals("only de_AT_variant", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-resolving1"));
|
||||
assertNull(theme.getMessages("messages", new Locale("de", "AT")).get("test.keycloak-12926-resolving1"));
|
||||
|
||||
Assert.assertEquals("only de_AT", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-resolving2"));
|
||||
Assert.assertNull(theme.getMessages("messages", new Locale("de")).get("test.keycloak-12926-resolving2"));
|
||||
assertEquals("only de_AT", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-resolving2"));
|
||||
assertNull(theme.getMessages("messages", new Locale("de")).get("test.keycloak-12926-resolving2"));
|
||||
|
||||
Assert.assertEquals("only de", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-only_de"));
|
||||
Assert.assertNull(theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926-only_de"));
|
||||
assertEquals("only de", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-only_de"));
|
||||
assertNull(theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926-only_de"));
|
||||
|
||||
Assert.assertEquals("fallback en", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-resolving3"));
|
||||
Assert.assertEquals("fallback en", theme.getMessages("messages", new Locale("de", "AT")).get("test.keycloak-12926-resolving3"));
|
||||
Assert.assertEquals("fallback en", theme.getMessages("messages", new Locale("de")).get("test.keycloak-12926-resolving3"));
|
||||
Assert.assertNull(theme.getMessages("messages", Locale.ENGLISH).get("fallback en"));
|
||||
assertEquals("fallback en", theme.getMessages("messages", new Locale("de", "AT", "variant")).get("test.keycloak-12926-resolving3"));
|
||||
assertEquals("fallback en", theme.getMessages("messages", new Locale("de", "AT")).get("test.keycloak-12926-resolving3"));
|
||||
assertEquals("fallback en", theme.getMessages("messages", new Locale("de")).get("test.keycloak-12926-resolving3"));
|
||||
assertNull(theme.getMessages("messages", Locale.ENGLISH).get("fallback en"));
|
||||
|
||||
} catch (IOException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
|
|
|
@ -374,6 +374,7 @@ public class KeycloakServer {
|
|||
long start = System.currentTimeMillis();
|
||||
|
||||
ResteasyDeployment deployment = new ResteasyDeployment();
|
||||
|
||||
deployment.setApplicationClass(KeycloakApplication.class.getName());
|
||||
|
||||
Builder builder = Undertow.builder()
|
||||
|
|
Loading…
Reference in a new issue