blob: 62d5ec29435a3f59ed6651384c3aaabf3d95e175 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.clerezza.platform.scripting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.clerezza.platform.content.DiscobitsHandler;
import org.apache.clerezza.platform.graphprovider.content.ContentGraphProvider;
import org.apache.clerezza.rdf.core.LiteralFactory;
import org.apache.clerezza.rdf.core.MGraph;
import org.apache.clerezza.rdf.core.NonLiteral;
import org.apache.clerezza.rdf.core.Resource;
import org.apache.clerezza.rdf.core.TypedLiteral;
import org.apache.clerezza.rdf.core.UriRef;
import org.apache.clerezza.rdf.core.access.TcManager;
import org.apache.clerezza.rdf.core.serializedform.Parser;
import org.apache.clerezza.rdf.ontologies.SCRIPT;
import org.apache.clerezza.rdf.utils.GraphNode;
/**
* This class executes scripts, binds {@code ScriptEngineFactory}ies
* of installed {@code ScriptEngine}s.
*
* It provides a facade to the installed {@code ScriptEngine}s
* that implement {@code javax.script.ScriptEngine}.
*
* The services {@code ContentGraphProvider}, {@code DiscobitsHandler}, and
* {@code TcManager} are provided to the ScriptEngine's eval method through
* the parameter {@code Bindings}. In a script, they are accessible under the
* names contentGraphProvider, contentHandler, and tcManager respectively.
*
* @scr.component
* @scr.service
* interface="org.apache.clerezza.platform.scripting.ScriptExecution"
* @scr.reference name="scriptEngineFactory" cardinality="0..n"
* interface="javax.script.ScriptEngineFactory"
*
* @author hasan, daniel
*
* @see javax.script.ScriptEngineManager
* @see javax.script.ScriptEngine
*/
public class ScriptExecution {
/**
* @scr.reference
*/
private ContentGraphProvider cgProvider;
/**
* @scr.reference
*/
private DiscobitsHandler contentHandler;
/**
* @scr.reference
*/
private TcManager tcManager;
/**
* @scr.reference
*/
private Parser parser;
private Map<ScriptLanguageDescription, List<ScriptEngineFactory>>
languageToFactoryMap =
new HashMap<ScriptLanguageDescription, List<ScriptEngineFactory>>();
private static final Logger logger = LoggerFactory.getLogger(ScriptExecution.class);
/**
* @see #execute(org.apache.clerezza.rdf.core.NonLiteral, javax.script.Bindings)
*/
public Object execute(NonLiteral scriptResource)
throws ScriptException, NoEngineException {
return execute(scriptResource, null);
}
/**
* Executes the specified {@code scriptResource}.
*
* The output is either a GraphNode or a response with
* the media type specified by the producedType property
* of the associated script.
*
* GraphNodes can be rendered by Renderlets registered for the type
* of the GraphNode.
*
* @param scriptResource
* The resource (URI).
* @param bindings
* Name/Value pairs of Java {@code Object}s made accessible
* to script
* @return
* The value returned from the execution of the script.
* Either a GraphNode or a Response.
* @throws ScriptException
* If an error occurrs in the script.
* @throws NoEngineException
* If no engine can be found
*
* @see org.apache.clerezza.rdf.ontologies.SCRIPT#Script
* @see org.apache.clerezza.rdf.ontologies.SCRIPT#producedType
* @see org.apache.clerezza.platform.typerendering.Renderlet
*/
public Object execute(NonLiteral scriptResource, Bindings bindings)
throws ScriptException, NoEngineException {
MGraph contentGraph = cgProvider.getContentGraph();
String scriptString = null;
String scriptLanguage = null;
String scriptLanguageVersion = null;
String scriptProducedType = "text/plain";
GraphNode scriptNode = new GraphNode(scriptResource, contentGraph);
Iterator<Resource> it = scriptNode.getObjects(SCRIPT.scriptLanguage);
scriptLanguage = LiteralFactory.getInstance().createObject(
String.class, (TypedLiteral) it.next());
it = scriptNode.getObjects(SCRIPT.scriptLanguageVersion);
scriptLanguageVersion = LiteralFactory.getInstance().createObject(
String.class, (TypedLiteral) it.next());
it = scriptNode.getObjects(SCRIPT.producedType);
if(it.hasNext()) {
scriptProducedType = LiteralFactory.getInstance().createObject(
String.class, (TypedLiteral) it.next());
}
scriptString = new String(
contentHandler.getData((UriRef) scriptResource));
Object result = execute(
scriptString, bindings, scriptLanguage, scriptLanguageVersion);
if (result instanceof GraphNode) {
return result;
}
return Response.ok(result, MediaType.valueOf(scriptProducedType)).build();
}
/**
* Executes the specified {@code script} with the {@code ScriptEngine}
* that is registered for {@code language} and {@code languageVersion}.
* <p>
* The default {@link ScriptContext} for the {@link ScriptEngine} is used.
* It uses {@code bindings} as the {@link ScriptContext#ENGINE_SCOPE}
* Bindings of the ScriptEngine during the script execution.
* Three new attributes are added to the bindings: contentGraphProvider,
* contentHandler, and tcManager.
* The Reader, Writer and non-ENGINE_SCOPE Bindings of the default
* ScriptContext are used. The ENGINE_SCOPE Bindings of the ScriptEngine
* is not changed, and its mappings are unaltered by the script execution.
* </p>
*
* @param script
* The script language source to be executed.
* @param bindings
* The Bindings of attributes to be used for script execution.
* @param language
* The script language.
* It is used to determine the ScriptEngine.
* @param languageVersion
* The version of the script language.
* It is used to determine the ScriptEngine.
* @return
* The value returned from the execution of {@code script}.
* @throws ScriptException
* If an error occurrs in the script.
* @throws NoEngineException
* If no engine can be found for the specified language
*
* @see javax.script.ScriptEngine#eval(java.lang.String, javax.script.Bindings)
*/
public Object execute(String script, Bindings bindings,
String language, String languageVersion)
throws ScriptException, NoEngineException {
ScriptEngine engine = getScriptEngine(language, languageVersion);
if (engine == null) {
logger.warn("Cannot execute script: " +
"No engine available for language {}({})",
language, languageVersion);
throw new NoEngineException(new ScriptLanguageDescription(language,
languageVersion));
}
if (bindings == null) {
bindings = engine.createBindings();
}
addBindings(bindings);
return engine.eval(script, bindings);
}
private void addBindings(Bindings bindings) {
bindings.put("contentGraphProvider", cgProvider);
bindings.put("contentHandler", contentHandler);
bindings.put("tcManager", tcManager);
bindings.put("parser", parser);
}
private ScriptEngine getScriptEngine(String language, String languageVersion) {
List<ScriptEngineFactory> factoryList = null;
try {
factoryList = languageToFactoryMap.get(
new ScriptLanguageDescription(language, languageVersion));
} catch (IllegalArgumentException ex) {
return null;
}
if (factoryList == null || factoryList.isEmpty()) {
return null;
}
return factoryList.get(factoryList.size() - 1).getScriptEngine();
}
/**
* Returns all installed script language descriptions.
*
* @return
* All script language descriptions
* of the currently installed script engines.
*
* @see ScriptLanguageDescription
*/
public Iterator<ScriptLanguageDescription> getInstalledScriptLanguages() {
return languageToFactoryMap.keySet().iterator();
}
/**
* Binds a ScriptEngineFactory.
* If script engine factories for the same language and/or language version
* already exist, they are shadowed.
*
* @param factory
* the script engine factory to bind
*/
protected void bindScriptEngineFactory(ScriptEngineFactory factory) {
String language = factory.getLanguageName();
String languageVersion = factory.getLanguageVersion();
addScriptEngineFactory(
new ScriptLanguageDescription(language, languageVersion),
factory);
}
private void addScriptEngineFactory(ScriptLanguageDescription language,
ScriptEngineFactory factory) {
List<ScriptEngineFactory> factoryList = languageToFactoryMap.get(language);
if (factoryList == null) {
factoryList = new ArrayList<ScriptEngineFactory>();
} else {
if (factoryList.contains(factory)) {
return;
}
}
factoryList.add(factory);
languageToFactoryMap.put(language, factoryList);
}
/**
* Unbinds a ScriptEngineFactory.
*
* @param factory
* the script engine factory to unbind
*/
protected void unbindScriptEngineFactory(ScriptEngineFactory factory) {
String language = factory.getLanguageName();
String languageVersion = factory.getLanguageVersion();
removeScriptEngineFactory(
new ScriptLanguageDescription(language, languageVersion),
factory);
}
private void removeScriptEngineFactory(ScriptLanguageDescription language,
ScriptEngineFactory factory) {
List<ScriptEngineFactory> factoryList = languageToFactoryMap.get(language);
if (factoryList != null) {
factoryList.remove(factory);
if (factoryList.isEmpty()) {
languageToFactoryMap.remove(language);
}
}
}
}