Java tutorial
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2013-2015 ForgeRock AS. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * http://forgerock.org/license/CDDLv1.0.html * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at http://forgerock.org/license/CDDLv1.0.html * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" */ package org.forgerock.openidm.script.impl; import static org.forgerock.json.resource.Responses.newActionResponse; import static org.forgerock.util.promise.Promises.newResultPromise; import java.io.File; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.script.ScriptException; import javax.script.SimpleBindings; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang3.StringUtils; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.ConfigurationPolicy; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.ReferencePolicy; import org.apache.felix.scr.annotations.References; import org.apache.felix.scr.annotations.Service; import org.forgerock.audit.events.AuditEvent; import org.forgerock.openidm.router.IDMConnectionFactory; import org.forgerock.openidm.script.ResourceFunctions; import org.forgerock.services.context.Context; import org.forgerock.json.JsonValue; import org.forgerock.json.JsonValueException; import org.forgerock.json.crypto.JsonCrypto; import org.forgerock.json.crypto.JsonCryptoException; import org.forgerock.json.resource.ActionRequest; import org.forgerock.json.resource.ActionResponse; import org.forgerock.json.resource.BadRequestException; import org.forgerock.json.resource.ConnectionFactory; import org.forgerock.json.resource.CreateRequest; import org.forgerock.json.resource.DeleteRequest; import org.forgerock.json.resource.ForbiddenException; import org.forgerock.json.resource.InternalServerErrorException; import org.forgerock.json.resource.NotSupportedException; import org.forgerock.json.resource.PatchRequest; import org.forgerock.json.resource.QueryRequest; import org.forgerock.json.resource.QueryResourceHandler; import org.forgerock.json.resource.QueryResponse; import org.forgerock.json.resource.ReadRequest; import org.forgerock.json.resource.RequestHandler; import org.forgerock.json.resource.Requests; import org.forgerock.json.resource.ResourceResponse; import org.forgerock.json.resource.ResourceException; import org.forgerock.json.resource.ServiceUnavailableException; import org.forgerock.json.resource.UpdateRequest; import org.forgerock.openidm.config.enhanced.EnhancedConfig; import org.forgerock.openidm.core.IdentityServer; import org.forgerock.openidm.core.ServerConstants; import org.forgerock.openidm.crypto.CryptoService; import org.forgerock.openidm.quartz.impl.ExecutionException; import org.forgerock.openidm.quartz.impl.ScheduledService; import org.forgerock.script.Script; import org.forgerock.script.ScriptEntry; import org.forgerock.script.engine.ScriptEngineFactory; import org.forgerock.script.exception.ScriptCompilationException; import org.forgerock.script.exception.ScriptThrownException; import org.forgerock.script.registry.ScriptRegistryImpl; import org.forgerock.script.scope.Function; import org.forgerock.script.scope.FunctionFactory; import org.forgerock.script.scope.Parameter; import org.forgerock.script.source.DirectoryContainer; import org.forgerock.script.source.SourceUnit; import org.forgerock.util.promise.Promise; import org.ops4j.pax.swissbox.extender.BundleWatcher; import org.ops4j.pax.swissbox.extender.ManifestEntry; import org.osgi.framework.Constants; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ @Component(name = ScriptRegistryService.PID, policy = ConfigurationPolicy.REQUIRE, metatype = true, description = "OpenIDM Script Registry Service", immediate = true) @Service @Properties({ @Property(name = Constants.SERVICE_VENDOR, value = ServerConstants.SERVER_VENDOR_NAME), @Property(name = Constants.SERVICE_DESCRIPTION, value = "OpenIDM Script Registry Service"), @Property(name = ServerConstants.ROUTER_PREFIX, value = "/script*") }) @References({ @Reference(name = "CryptoServiceReference", referenceInterface = CryptoService.class, bind = "bindCryptoService", unbind = "unbindCryptoService", cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC), @Reference(name = "IDMConnectionFactoryReference", referenceInterface = IDMConnectionFactory.class, bind = "setConnectionFactory", unbind = "unsetConnectionFactory", cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC), @Reference(name = "ScriptEngineFactoryReference", referenceInterface = ScriptEngineFactory.class, bind = "addingEntries", unbind = "removingEntries", cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC), @Reference(name = "FunctionReference", referenceInterface = Function.class, bind = "bindFunction", unbind = "unbindFunction", cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC, target = "(" + ScriptRegistryService.SCRIPT_NAME + "=*)") }) public class ScriptRegistryService extends ScriptRegistryImpl implements RequestHandler, ScheduledService { public static final Set<String> reservedNames; static { Set<String> _reservedNames = new HashSet<String>(15); _reservedNames.add("create"); _reservedNames.add("read"); _reservedNames.add("update"); _reservedNames.add("patch"); _reservedNames.add("query"); _reservedNames.add("delete"); _reservedNames.add("action"); _reservedNames.add("encrypt"); _reservedNames.add("decrypt"); _reservedNames.add("isEncrypted"); _reservedNames.add("isHashed"); _reservedNames.add("matches"); for (IdentityServerFunctions f : IdentityServerFunctions.values()) { _reservedNames.add(f.name()); } reservedNames = Collections.unmodifiableSet(_reservedNames); } // TODO Move to public package public static final String SCRIPT_NAME = "org.forgerock.openidm.script.name"; // Public Constants public static final String PID = "org.forgerock.openidm.script"; private ConnectionFactory connectionFactory = null; /** * Setup logging for the {@link ScriptRegistryService}. */ // private static final LocalizedLogger logger = // LocalizedLogger.getLocalizedLogger(ScriptRegistryService.class); private static final Logger logger = LoggerFactory.getLogger(ScriptRegistryService.class); private static final String PROP_IDENTITY_SERVER = "identityServer"; private static final String PROP_OPENIDM = "openidm"; private static final String PROP_CONSOLE = "console"; private static final String SOURCE_DIRECTORY = "directory"; private static final String SOURCE_FILE = "file"; private static final String SOURCE_SUBDIRECTORIES = "subdirectories"; private static final String SOURCE_VISIBILITY = "visibility"; private static final String SOURCE_TYPE = "type"; private static final String SOURCE_GLOBALS = "globals"; /** Enhanced configuration service. */ @Reference(policy = ReferencePolicy.DYNAMIC) private EnhancedConfig enhancedConfig; private final ConcurrentMap<String, Object> openidm = new ConcurrentHashMap<String, Object>(); private static final ConcurrentMap<String, Object> propertiesCache = new ConcurrentHashMap<String, Object>(); private enum Action { compile, eval } private BundleWatcher<ManifestEntry> manifestWatcher; @Activate protected void activate(ComponentContext context) throws Exception { JsonValue configuration = enhancedConfig.getConfigurationAsJson(context); setConfiguration(configuration.required().asMap()); HashMap<String, Object> identityServer = new HashMap<String, Object>(); for (IdentityServerFunctions f : IdentityServerFunctions.values()) { identityServer.put(f.name(), f); } Map<String, Object> console = new HashMap<String, Object>(); console.put("log", new Function<Void>() { @Override public Void call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length > 0) { if (arguments[0] instanceof String) { System.out.println((String) arguments[0]); } } return null; } }); put(PROP_IDENTITY_SERVER, identityServer); put(PROP_CONSOLE, console); put(PROP_OPENIDM, openidm); JsonValue properties = configuration.get("properties"); if (properties.isMap()) { for (Map.Entry<String, Object> entry : properties.asMap().entrySet()) { if (PROP_IDENTITY_SERVER.equals(entry.getKey()) || PROP_OPENIDM.equals(entry.getKey()) || PROP_CONSOLE.equals(entry.getKey())) { continue; } put(entry.getKey(), entry.getValue()); } } try { JsonValue sources = configuration.get("sources"); if (!sources.isNull()) { // Must reverse default-first config since ScriptRegistryImpl loads scripts from the first dir it hits. List<String> keys = new ArrayList(sources.keys()); Collections.reverse(keys); for (String key : keys) { JsonValue source = sources.get(key); String directory = source.get(SOURCE_DIRECTORY).asString(); URL directoryURL = (new File(directory)).toURI().toURL(); /* TODO: Support addition config properties (currently set to defaults in commons) JsonValue subDirValue = source.get(SOURCE_SUBDIRECTORIES).defaultTo("auto-true"); boolean subdirectories = true; if (subDirValue.isBoolean()) { subdirectories = subDirValue.asBoolean(); } else { subdirectories = Boolean.parseBoolean(subDirValue.asString()); } String type = source.get(SOURCE_TYPE).defaultTo("auto-detect").asString(); String visibility = source.get(SOURCE_VISIBILITY).defaultTo("public").asString(); */ DirectoryContainer dc = new DirectoryContainer(key, directoryURL); addSourceUnit(dc); } } } catch (Exception e) { logger.error("Error loading sources", e); throw e; } // OPENIDM-1746 Set the script registry class loader to this class, so that any libraries // bundled as an OSGI-Fragment attached to this bundle will be "seen" by scripts. setRegistryLevelScriptClassLoader(this.getClass().getClassLoader()); /* * manifestWatcher = new BundleWatcher<ManifestEntry>(context, new * ScriptEngineManifestScanner(), null); manifestWatcher.start(); */ logger.info("OpenIDM Script Service component is activated."); } @Modified protected void modified(ComponentContext context) { JsonValue configuration = enhancedConfig.getConfigurationAsJson(context); setConfiguration(configuration.required().asMap()); propertiesCache.clear(); Set<String> keys = null != getBindings() ? new HashSet<String>(getBindings().keySet()) : Collections.<String>emptySet(); keys.remove(PROP_OPENIDM); keys.remove(PROP_IDENTITY_SERVER); keys.remove(PROP_CONSOLE); JsonValue properties = configuration.get("properties"); if (properties.isMap()) { for (Map.Entry<String, Object> entry : properties.asMap().entrySet()) { if (PROP_IDENTITY_SERVER.equals(entry.getKey()) || PROP_OPENIDM.equals(entry.getKey()) || PROP_CONSOLE.equals(entry.getKey())) { continue; } put(entry.getKey(), entry.getValue()); keys.remove(entry.getKey()); } } if (!keys.isEmpty()) { for (String name : keys) { getBindings().remove(name); } } logger.info("OpenIDM Script Service component is modified."); } @Deactivate protected void deactivate(ComponentContext context) { if (null != manifestWatcher) { manifestWatcher.stop(); } propertiesCache.clear(); openidm.clear(); setBindings(null); logger.info("OpenIDM Script Service component is deactivated."); } public void setConnectionFactory(IDMConnectionFactory connectionFactory) { openidm.put("create", ResourceFunctions.newCreateFunction(connectionFactory)); openidm.put("read", ResourceFunctions.newReadFunction(connectionFactory)); openidm.put("update", ResourceFunctions.newUpdateFunction(connectionFactory)); openidm.put("patch", ResourceFunctions.newPatchFunction(connectionFactory)); openidm.put("query", ResourceFunctions.newQueryFunction(connectionFactory)); openidm.put("delete", ResourceFunctions.newDeleteFunction(connectionFactory)); openidm.put("action", ResourceFunctions.newActionFunction(connectionFactory)); this.connectionFactory = connectionFactory; logger.info("Resource functions are enabled"); } public void unsetConnectionFactory(IDMConnectionFactory connectionFactory) { openidm.remove("create"); openidm.remove("read"); openidm.remove("update"); openidm.remove("patch"); openidm.remove("query"); openidm.remove("delete"); openidm.remove("action"); this.connectionFactory = null; logger.info("Resource functions are disabled"); } protected void bindCryptoService(final CryptoService cryptoService) { // hash(any value, string algorithm) openidm.put("hash", new Function<JsonValue>() { static final long serialVersionUID = 1L; public JsonValue call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length == 2) { JsonValue value = null; String algorithm = null; if (arguments[0] instanceof Map || arguments[0] instanceof List || arguments[0] instanceof String || arguments[0] instanceof Number || arguments[0] instanceof Boolean) { value = new JsonValue(arguments[0]); } else if (arguments[0] instanceof JsonValue) { value = (JsonValue) arguments[0]; } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("hash", arguments)); } if (arguments[1] instanceof String) { algorithm = (String) arguments[1]; } else if (arguments[1] == null) { algorithm = ServerConstants.SECURITY_CRYPTOGRAPHY_DEFAULT_HASHING_ALGORITHM; } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("hash", arguments)); } try { return cryptoService.hash(value, algorithm); } catch (JsonCryptoException e) { throw new InternalServerErrorException(e.getMessage(), e); } } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("hash", arguments)); } } }); // encrypt(any value, string cipher, string alias) openidm.put("encrypt", new Function<JsonValue>() { static final long serialVersionUID = 1L; public JsonValue call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length == 3) { JsonValue value = null; String cipher = null; String alias = null; if (arguments[0] instanceof Map || arguments[0] instanceof List || arguments[0] instanceof String || arguments[0] instanceof Number || arguments[0] instanceof Boolean) { value = new JsonValue(arguments[0]); } else if (arguments[0] instanceof JsonValue) { value = (JsonValue) arguments[0]; } else { throw new NoSuchMethodException( FunctionFactory.getNoSuchMethodMessage("encrypt", arguments)); } if (arguments[1] instanceof String) { cipher = (String) arguments[1]; } else if (arguments[1] == null) { cipher = ServerConstants.SECURITY_CRYPTOGRAPHY_DEFAULT_CIPHER; } else { throw new NoSuchMethodException( FunctionFactory.getNoSuchMethodMessage("encrypt", arguments)); } if (arguments[2] instanceof String) { alias = (String) arguments[2]; } else { throw new NoSuchMethodException( FunctionFactory.getNoSuchMethodMessage("encrypt", arguments)); } try { return cryptoService.encrypt(value, cipher, alias); } catch (JsonCryptoException e) { throw new InternalServerErrorException(e.getMessage(), e); } } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("encrypt", arguments)); } } }); // decrypt(any value) openidm.put("decrypt", new Function<JsonValue>() { static final long serialVersionUID = 1L; public JsonValue call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length == 1 && (arguments[0] instanceof Map || arguments[0] instanceof JsonValue)) { return cryptoService.decrypt(arguments[0] instanceof JsonValue ? (JsonValue) arguments[0] : new JsonValue(arguments[0])); } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("decrypt", arguments)); } } }); // isEncrypted(any value) openidm.put("isEncrypted", new Function<Boolean>() { static final long serialVersionUID = 1L; public Boolean call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments == null || arguments.length == 0) { return false; } else if (arguments.length == 1) { return JsonCrypto.isJsonCrypto(arguments[0] instanceof JsonValue ? (JsonValue) arguments[0] : new JsonValue(arguments[0])); } else { throw new NoSuchMethodException( FunctionFactory.getNoSuchMethodMessage("isEncrypted", arguments)); } } }); // isHashed(any value) openidm.put("isHashed", new Function<Boolean>() { static final long serialVersionUID = 1L; public Boolean call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments == null || arguments.length == 0) { return false; } else if (arguments.length == 1) { return cryptoService.isHashed(arguments[0] instanceof JsonValue ? (JsonValue) arguments[0] : new JsonValue(arguments[0])); } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("isHashed", arguments)); } } }); // matches(String plaintext, any value) openidm.put("matches", new Function<Boolean>() { static final long serialVersionUID = 1L; public Boolean call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments == null || arguments.length == 0 || arguments.length == 1) { return false; } else if (arguments.length == 2) { try { return cryptoService.matches(arguments[0].toString(), arguments[1] instanceof JsonValue ? (JsonValue) arguments[1] : new JsonValue(arguments[1])); } catch (JsonCryptoException e) { throw new InternalServerErrorException(e.getMessage(), e); } } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage("matches", arguments)); } } }); logger.info("Crypto functions are enabled"); } protected void unbindCryptoService(final CryptoService function) { openidm.remove("encrypt"); openidm.remove("decrypt"); openidm.remove("isEncrypted"); openidm.remove("isHashed"); openidm.remove("matches"); logger.info("Crypto functions are disabled"); } protected void bindFunction(final Function function, Map properties) { Object name = properties.get(SCRIPT_NAME); if (name instanceof String && StringUtils.isNotBlank((String) name) && !reservedNames.contains((String) name)) { openidm.put((String) name, function); logger.info("openidm.{} function is enabled", name); } } protected void unbindFunction(final Function function, Map properties) { Object name = properties.get(SCRIPT_NAME); if (name instanceof String && StringUtils.isNotBlank((String) name) && !reservedNames.contains((String) name)) { openidm.remove(name, function); logger.info("openidm.{} function is disabled", name); } } @Override public ScriptEntry takeScript(JsonValue script) throws ScriptException { JsonValue scriptConfig = script.clone(); if (scriptConfig.get(SourceUnit.ATTR_NAME).isNull()) { JsonValue file = scriptConfig.get(SOURCE_FILE); JsonValue source = scriptConfig.get(SourceUnit.ATTR_SOURCE); if (!file.isNull()) { // If we do not have a name use the file. scriptConfig.put(SourceUnit.ATTR_NAME, file.asString()); } else if (!source.isNull()) { /* * We assign a name here otherwise ScriptRegistryImpl.takeScript() assigns a random UUID. * This results in a newly created and cached class on every invocation. */ try { MessageDigest md = MessageDigest.getInstance("SHA-1"); // digest source AND type as we could have identical source for different types String type = scriptConfig.get(SourceUnit.ATTR_TYPE).asString(); byte[] nameAndType = (source.asString() + type).getBytes(); byte[] digest = md.digest(nameAndType); String name = DatatypeConverter.printHexBinary(digest); scriptConfig.put(SourceUnit.ATTR_NAME, name); } catch (NoSuchAlgorithmException e) { // SHA-1 is a required implementation. This should never happen. logger.error( "Could not get SHA-1 MessageDigest instance. This should be implemented on any standard JVM.", e); } } } // Get and remove any defined globals JsonValue globals = scriptConfig.get(SOURCE_GLOBALS); scriptConfig.remove(SOURCE_GLOBALS); // Create the script entry ScriptEntry scriptEntry = super.takeScript(scriptConfig); // Add the globals (if any) to the script bindings if (!globals.isNull() && globals.isMap()) { for (String key : globals.keys()) { scriptEntry.put(key, globals.get(key)); } } return scriptEntry; } private static enum IdentityServerFunctions implements Function<Object> { getProperty { public Object call(Parameter scope, Function<?> callback, Object... arguments) throws ResourceException, NoSuchMethodException { boolean useCache = false; if (arguments.length < 1 || arguments.length > 3) { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(this.name(), arguments)); } if (arguments.length == 3) { useCache = (Boolean) arguments[2]; } if (arguments[0] instanceof String) { String name = (String) arguments[0]; Object result = null; if (useCache) { result = propertiesCache.get(name); } if (null == result) { Object defaultValue = arguments.length == 2 ? arguments[1] : null; result = IdentityServer.getInstance().getProperty(name, defaultValue, Object.class); propertiesCache.putIfAbsent(name, result); } return result; } else { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(this.name(), arguments)); } } }, getWorkingLocation { public Object call(Parameter scope, Function callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length != 0) { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(this.name(), arguments)); } return IdentityServer.getInstance().getWorkingLocation().getAbsolutePath(); } }, getProjectLocation { public Object call(Parameter scope, Function callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length != 0) { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(this.name(), arguments)); } return IdentityServer.getInstance().getProjectLocation().getAbsolutePath(); } }, getInstallLocation { public Object call(Parameter scope, Function callback, Object... arguments) throws ResourceException, NoSuchMethodException { if (arguments.length != 0) { throw new NoSuchMethodException(FunctionFactory.getNoSuchMethodMessage(this.name(), arguments)); } return IdentityServer.getInstance().getInstallLocation().getAbsolutePath(); } }; static final long serialVersionUID = 1L; } private boolean isSourceUnit(String name) { if (SourceUnit.ATTR_NAME.equals(name) || SourceUnit.ATTR_REVISION.equals(name) || SourceUnit.ATTR_SOURCE.equals(name) || SourceUnit.ATTR_TYPE.equals(name) || SourceUnit.ATTR_VISIBILITY.equals(name) || SourceUnit.AUTO_DETECT.equals(name) || SOURCE_FILE.equals(name) || SOURCE_GLOBALS.equals(name)) { return true; } return false; } // ----- Implementation of RequestHandler interface public Promise<ActionResponse, ResourceException> handleAction(final Context context, final ActionRequest request) { String resourcePath = request.getResourcePath(); JsonValue content = request.getContent(); Map<String, Object> bindings = new HashMap<String, Object>(); JsonValue config = new JsonValue(new HashMap<String, Object>()); ScriptEntry scriptEntry = null; try { if (resourcePath == null || "".equals(resourcePath)) { for (String key : content.keys()) { if (isSourceUnit(key)) { config.put(key, content.get(key).getObject()); } else { bindings.put(key, content.get(key).getObject()); } } // The script will be in the request content scriptEntry = takeScript(config); // Add any additional parameters to the map of bindings bindings.putAll(request.getAdditionalParameters()); } else { throw new NotSupportedException("Actions are not supported for resource instances"); } switch (request.getActionAsEnum(Action.class)) { case compile: if (scriptEntry.isActive()) { // just get the script - compilation technically happened above in takeScript scriptEntry.getScript(context); return newActionResponse(new JsonValue(true)).asPromise(); } else { throw new ServiceUnavailableException(); } case eval: if (scriptEntry.isActive()) { Script script = scriptEntry.getScript(context); return newResultPromise( newActionResponse(new JsonValue(script.eval(new SimpleBindings(bindings))))); } else { throw new ServiceUnavailableException(); } default: throw new BadRequestException("Unrecognized action ID " + request.getAction()); } } catch (ResourceException e) { return e.asPromise(); } catch (ScriptCompilationException e) { return new BadRequestException(e.getMessage(), e).asPromise(); } catch (IllegalArgumentException e) { // from getActionAsEnum return new BadRequestException(e.getMessage(), e).asPromise(); } catch (Exception e) { return new InternalServerErrorException(e.getMessage(), e).asPromise(); } } public Promise<QueryResponse, ResourceException> handleQuery(final Context context, final QueryRequest request, final QueryResourceHandler handler) { final ResourceException e = new NotSupportedException("Query operations are not supported"); return e.asPromise(); } public Promise<ResourceResponse, ResourceException> handleRead(final Context context, final ReadRequest request) { final ResourceException e = new NotSupportedException("Read operations are not supported"); return e.asPromise(); } public Promise<ResourceResponse, ResourceException> handleCreate(final Context context, final CreateRequest request) { final ResourceException e = new NotSupportedException("Create operations are not supported"); return e.asPromise(); } public Promise<ResourceResponse, ResourceException> handleDelete(final Context context, final DeleteRequest request) { final ResourceException e = new NotSupportedException("Delete operations are not supported"); return e.asPromise(); } public Promise<ResourceResponse, ResourceException> handlePatch(final Context context, final PatchRequest request) { final ResourceException e = new NotSupportedException("Patch operations are not supported"); return e.asPromise(); } public Promise<ResourceResponse, ResourceException> handleUpdate(final Context context, final UpdateRequest request) { final ResourceException e = new NotSupportedException("Update operations are not supported"); return e.asPromise(); } @Override public void execute(Context context, Map<String, Object> scheduledContext) throws ExecutionException { try { String scriptName = (String) scheduledContext.get(CONFIG_NAME); JsonValue params = new JsonValue(scheduledContext).get(CONFIGURED_INVOKE_CONTEXT); JsonValue scriptValue = params.get("script").expect(Map.class).clone(); if (scriptValue.get(SourceUnit.ATTR_NAME).isNull()) { if (!scriptValue.get(SOURCE_FILE).isNull()) { scriptValue.put(SourceUnit.ATTR_NAME, scriptValue.get(SOURCE_FILE).getObject()); } } if (!scriptValue.isNull()) { ScriptEntry entry = takeScript(scriptValue); JsonValue input = params.get("input"); execScript(context, entry, input); } else { throw new ExecutionException("No valid script '" + scriptName + "' configured in schedule."); } } catch (JsonValueException jve) { throw new ExecutionException(jve); } catch (ScriptException e) { throw new ExecutionException(e); } catch (ResourceException e) { throw new ExecutionException(e); } } @Override public void auditScheduledService(final Context context, final AuditEvent auditEvent) throws ExecutionException { try { if (connectionFactory != null) { connectionFactory.getConnection().create(context, Requests.newCreateRequest("audit/access", auditEvent.getValue())); } } catch (ResourceException e) { logger.error("Unable to audit scheduled service {}", auditEvent.toString()); throw new ExecutionException("Unable to audit scheduled service", e); } } private void execScript(Context context, ScriptEntry script, JsonValue value) throws ForbiddenException, InternalServerErrorException { if (null != script && script.isActive()) { Script executable = script.getScript(context); executable.put("object", value.getObject()); try { executable.eval(); // allows direct modification to the object } catch (ScriptThrownException ste) { throw new ForbiddenException(ste.getValue().toString()); } catch (ScriptException se) { throw new InternalServerErrorException("script encountered exception", se); } } } }