/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.scripting.javascript.internal;
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
import com.liferay.portal.kernel.scripting.ScriptingException;
import com.liferay.portal.kernel.scripting.ScriptingExecutor;
import com.liferay.portal.scripting.BaseScriptingExecutor;
import com.liferay.portal.scripting.javascript.configuration.JavaScriptExecutorConfiguration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
/**
* @author Alberto Montero
*/
@Component(
immediate = true,
property = {"scripting.language=" + JavaScriptExecutor.LANGUAGE},
service = ScriptingExecutor.class
)
public class JavaScriptExecutor extends BaseScriptingExecutor {
public static final String LANGUAGE = "javascript";
@Override
public Map<String, Object> eval(
Set<String> allowedClasses, Map<String, Object> inputObjects,
Set<String> outputNames, String script)
throws ScriptingException {
Script compiledScript = getCompiledScript(script);
try {
Context context = Context.enter();
Scriptable scriptable = context.initStandardObjects();
context.setApplicationClassLoader(getClassLoader());
for (Map.Entry<String, Object> entry : inputObjects.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
ScriptableObject.putProperty(
scriptable, key, Context.javaToJS(value, scriptable));
}
if (allowedClasses != null) {
context.setClassShutter(
new JavaScriptClassVisibilityChecker(
allowedClasses, _forbiddenClassNames));
}
compiledScript.exec(context, scriptable);
if (outputNames == null) {
return null;
}
Map<String, Object> outputObjects = new HashMap<>();
for (String outputName : outputNames) {
Object property = ScriptableObject.getProperty(
scriptable, outputName);
if (property instanceof Wrapper) {
Wrapper wrapper = (Wrapper)property;
property = wrapper.unwrap();
}
outputObjects.put(outputName, property);
}
return outputObjects;
}
catch (Exception e) {
throw new ScriptingException(e.getMessage() + "\n\n", e);
}
finally {
Context.exit();
}
}
@Override
public String getLanguage() {
return LANGUAGE;
}
@Override
public ScriptingExecutor newInstance(boolean executeInSeparateThread) {
return new JavaScriptExecutor();
}
@Activate
@Modified
protected void activate(Map<String, Object> properties) {
JavaScriptExecutorConfiguration javaScriptExecutorConfiguration =
ConfigurableUtil.createConfigurable(
JavaScriptExecutorConfiguration.class, properties);
_forbiddenClassNames = new HashSet<>(
Arrays.asList(
javaScriptExecutorConfiguration.forbiddenClassNames()));
}
protected Script getCompiledScript(String script)
throws ScriptingException {
try {
Context context = Context.enter();
Class<?> clazz = getClass();
context.setApplicationClassLoader(clazz.getClassLoader());
return context.compileString(script, "script", 0, null);
}
catch (Exception e) {
throw new ScriptingException(e.getMessage() + "\n\n", e);
}
finally {
Context.exit();
}
}
private volatile Set<String> _forbiddenClassNames;
}