/* * JBoss, Home of Professional Open Source * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. */ package org.jboss.elasticsearch.tools.content; import java.util.Map; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleScriptContext; import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.common.xcontent.support.XContentMapValues; /** * Content preprocessor which runs script by some <a * href="http://docs.oracle.com/javase/6/docs/technotes/guides/scripting/">Java Scripting API</a> provided engine to * manipulate processed data. Example of configuration for this preprocessor: * * <pre> * { * "name" : "Script runner", * "class" : "org.jboss.elasticsearch.tools.content.ScriptingPreprocessor", * "settings" : { * "script_engine_name" : "JavaScript", * "script" : "data.put('v2', data.get('v')); data.put('c','con');" * } * } * </pre> * * Options are: * <ul> * <li><code>script_engine_name</code> - optional name of scripting engine. Defaults to <code>JavaScript</code>. * <li><code>script</code> - script code to run, {@link ScriptEngine#eval(String)} method is used. You can use variable * called <code>data</code> to manipulate processed data. * </ul> * <p> * <b>Note</b> that performance of this preprocessor depends on performance of scripting engine. It is always better to * use other existing specialized preprocessors for simple tasks like constant values setting, simple value copy etc. * * @author Vlastimil Elias (velias at redhat dot com) * @see StructuredContentPreprocessorFactory */ public class ScriptingPreprocessor extends StructuredContentPreprocessorBase { protected static final String CFG_SCRIPT_ENGINE_NAME_FIELD = "script_engine_name"; protected static final String CFG_SCRIPT_FIELD = "script"; protected String scriptEngineName; protected static ScriptEngineManager factory = new ScriptEngineManager(); protected String script; protected ScriptEngine engine; @Override public void init(Map<String, Object> settings) throws SettingsException { if (settings == null) { throw new SettingsException("'settings' section is not defined for preprocessor " + name); } script = XContentMapValues.nodeStringValue(settings.get(CFG_SCRIPT_FIELD), null); validateConfigurationStringNotEmpty(script, CFG_SCRIPT_FIELD); scriptEngineName = XContentMapValues.nodeStringValue(settings.get(CFG_SCRIPT_ENGINE_NAME_FIELD), "JavaScript"); validateConfigurationStringNotEmpty(scriptEngineName, CFG_SCRIPT_ENGINE_NAME_FIELD); engine = factory.getEngineByName(scriptEngineName); if (engine == null) { throw new SettingsException("No scripting engine is available for name " + scriptEngineName + " for preprocessor " + name); } } @Override public Map<String, Object> preprocessData(Map<String, Object> data, PreprocessChainContext chainContext) { ScriptContext newContext = new SimpleScriptContext(); Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE); engineScope.put("data", data); try { synchronized (engine) { engine.eval(script, newContext); } } catch (ScriptException e) { String warningMessage = "Script execution failed: " + e.getMessage(); addDataWarning(chainContext, warningMessage); logger.debug(warningMessage); } return data; } public String getScriptEngineName() { return scriptEngineName; } public String getScript() { return script; } }