parent
834a276767
commit
fb81242658
3 changed files with 56 additions and 12 deletions
|
@ -24,6 +24,7 @@ import javax.script.ScriptEngineManager;
|
|||
import javax.script.ScriptException;
|
||||
|
||||
import org.keycloak.models.ScriptModel;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
|
||||
/**
|
||||
* A {@link ScriptingProvider} that uses a {@link ScriptEngineManager} to evaluate scripts with a {@link ScriptEngine}.
|
||||
|
@ -32,14 +33,10 @@ import org.keycloak.models.ScriptModel;
|
|||
*/
|
||||
public class DefaultScriptingProvider implements ScriptingProvider {
|
||||
|
||||
private final ScriptEngineManager scriptEngineManager;
|
||||
private final DefaultScriptingProviderFactory factory;
|
||||
|
||||
DefaultScriptingProvider(ScriptEngineManager scriptEngineManager) {
|
||||
if (scriptEngineManager == null) {
|
||||
throw new IllegalStateException("scriptEngineManager must not be null!");
|
||||
}
|
||||
|
||||
this.scriptEngineManager = scriptEngineManager;
|
||||
DefaultScriptingProvider(DefaultScriptingProviderFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +66,7 @@ public class DefaultScriptingProvider implements ScriptingProvider {
|
|||
throw new IllegalArgumentException("script must not be null or empty");
|
||||
}
|
||||
|
||||
ScriptEngine engine = createPreparedScriptEngine(scriptModel);
|
||||
ScriptEngine engine = getPreparedScriptEngine(scriptModel);
|
||||
|
||||
if (engine instanceof Compilable) {
|
||||
return new CompiledEvaluatableScriptAdapter(scriptModel, tryCompile(scriptModel, (Compilable) engine));
|
||||
|
@ -99,13 +96,26 @@ public class DefaultScriptingProvider implements ScriptingProvider {
|
|||
/**
|
||||
* Looks-up a {@link ScriptEngine} with prepared {@link Bindings} for the given {@link ScriptModel Script}.
|
||||
*/
|
||||
private ScriptEngine createPreparedScriptEngine(ScriptModel script) {
|
||||
private ScriptEngine getPreparedScriptEngine(ScriptModel script) {
|
||||
// Try to lookup shared engine in the cache first
|
||||
if (factory.isEnableScriptEngineCache()) {
|
||||
ScriptEngine scriptEngine = factory.getScriptEngineCache().get(script.getMimeType());
|
||||
if (scriptEngine != null) return scriptEngine;
|
||||
}
|
||||
|
||||
ScriptEngine scriptEngine = lookupScriptEngineFor(script);
|
||||
|
||||
if (scriptEngine == null) {
|
||||
throw new IllegalStateException("Could not find ScriptEngine for script: " + script);
|
||||
}
|
||||
|
||||
ServicesLogger.LOGGER.scriptEngineCreated(scriptEngine.getFactory().getEngineName(), scriptEngine.getFactory().getEngineVersion(), script.getMimeType());
|
||||
|
||||
// Nashorn scriptEngine is ok to cache and share across multiple threads
|
||||
if (factory.isEnableScriptEngineCache()) {
|
||||
factory.getScriptEngineCache().put(script.getMimeType(), scriptEngine);
|
||||
}
|
||||
|
||||
return scriptEngine;
|
||||
}
|
||||
|
||||
|
@ -116,7 +126,7 @@ public class DefaultScriptingProvider implements ScriptingProvider {
|
|||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(DefaultScriptingProvider.class.getClassLoader());
|
||||
return scriptEngineManager.getEngineByMimeType(script.getMimeType());
|
||||
return factory.getScriptEngineManager().getEngineByMimeType(script.getMimeType());
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(cl);
|
||||
|
|
|
@ -16,10 +16,16 @@
|
|||
*/
|
||||
package org.keycloak.scripting;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
/**
|
||||
|
@ -27,20 +33,40 @@ import javax.script.ScriptEngineManager;
|
|||
*/
|
||||
public class DefaultScriptingProviderFactory implements ScriptingProviderFactory {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultScriptingProviderFactory.class);
|
||||
|
||||
static final String ID = "script-based-auth";
|
||||
|
||||
private ScriptEngineManager scriptEngineManager;
|
||||
|
||||
private boolean enableScriptEngineCache;
|
||||
|
||||
// Key is mime-type. Value is engine for the particular mime-type. Cache can be used when the scriptEngine can be shared across multiple threads / requests (which is the case for nashorn)
|
||||
private Map<String, ScriptEngine> scriptEngineCache;
|
||||
|
||||
@Override
|
||||
public ScriptingProvider create(KeycloakSession session) {
|
||||
lazyInit();
|
||||
|
||||
return new DefaultScriptingProvider(scriptEngineManager);
|
||||
return new DefaultScriptingProvider(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
//NOOP
|
||||
this.enableScriptEngineCache = config.getBoolean("enable-script-engine-cache", true);
|
||||
logger.debugf("Enable script engine cache: %b", this.enableScriptEngineCache);
|
||||
}
|
||||
|
||||
ScriptEngineManager getScriptEngineManager() {
|
||||
return scriptEngineManager;
|
||||
}
|
||||
|
||||
boolean isEnableScriptEngineCache() {
|
||||
return enableScriptEngineCache;
|
||||
}
|
||||
|
||||
Map<String, ScriptEngine> getScriptEngineCache() {
|
||||
return scriptEngineCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,6 +89,9 @@ public class DefaultScriptingProviderFactory implements ScriptingProviderFactory
|
|||
synchronized (this) {
|
||||
if (scriptEngineManager == null) {
|
||||
scriptEngineManager = new ScriptEngineManager();
|
||||
if (enableScriptEngineCache) {
|
||||
scriptEngineCache = new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -462,4 +462,9 @@ public interface ServicesLogger extends BasicLogger {
|
|||
@LogMessage(level = ERROR)
|
||||
@Message(id=105, value="Response_mode 'query.jwt' is allowed only when the authorization response token is encrypted")
|
||||
void responseModeQueryJwtNotAllowed();
|
||||
|
||||
@LogMessage(level = INFO)
|
||||
@Message(id=106, value="Created script engine '%s', version '%s' for the mime type '%s'")
|
||||
@Once
|
||||
void scriptEngineCreated(String engineName, String engineVersion, String mimeType);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue