mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-16 17:55:15 +00:00
- similar pattern to existing script bean extension support - new root model helper objects and custom methods can be added via spring configuration Cleanup of script extension spring support Fix to thread safety of configured script extension beans that use the Scopable interface git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5369 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
381 lines
13 KiB
Java
381 lines
13 KiB
Java
/*
|
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
* As a special exception to the terms and conditions of version 2.0 of
|
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
* FLOSS exception. You should have recieved a copy of the text describing
|
|
* the FLOSS exception, and it is also available here:
|
|
* http://www.alfresco.com/legal/licensing"
|
|
*/
|
|
package org.alfresco.repo.jscript;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.Reader;
|
|
import java.io.StringReader;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.service.ServiceRegistry;
|
|
import org.alfresco.service.cmr.repository.ContentReader;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.ScriptException;
|
|
import org.alfresco.service.cmr.repository.ScriptImplementation;
|
|
import org.alfresco.service.cmr.repository.ScriptLocation;
|
|
import org.alfresco.service.cmr.repository.ScriptImplementation;
|
|
import org.alfresco.service.cmr.repository.ScriptService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.ParameterCheck;
|
|
import org.apache.log4j.Logger;
|
|
import org.mozilla.javascript.Context;
|
|
import org.mozilla.javascript.EvaluatorException;
|
|
import org.mozilla.javascript.NativeArray;
|
|
import org.mozilla.javascript.Scriptable;
|
|
import org.mozilla.javascript.ScriptableObject;
|
|
import org.mozilla.javascript.Wrapper;
|
|
|
|
/**
|
|
* Implementation of the ScriptService using the Rhino JavaScript engine.
|
|
*
|
|
* @author Kevin Roast
|
|
*/
|
|
public class RhinoScriptService implements ScriptService
|
|
{
|
|
private static final Logger logger = Logger.getLogger(RhinoScriptService.class);
|
|
|
|
/** Repository Service Registry */
|
|
private ServiceRegistry services;
|
|
|
|
/** List of global scripts */
|
|
private List<ScriptImplementation> globalScripts = new ArrayList<ScriptImplementation>();
|
|
|
|
/**
|
|
* Set the Service Registry
|
|
*
|
|
* @param service registry
|
|
*/
|
|
public void setServiceRegistry(ServiceRegistry services)
|
|
{
|
|
this.services = services;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.repository.ScriptService#registerScript(java.lang.Object)
|
|
*/
|
|
public void registerScript(ScriptImplementation script)
|
|
{
|
|
this.globalScripts.add(script);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.repository.ScriptService#executeScript(java.lang.String, java.util.Map)
|
|
*/
|
|
public Object executeScript(String scriptClasspath, Map<String, Object> model)
|
|
throws ScriptException
|
|
{
|
|
if (scriptClasspath == null)
|
|
{
|
|
throw new IllegalArgumentException("Script ClassPath is mandatory.");
|
|
}
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Executing script: " + scriptClasspath);
|
|
}
|
|
|
|
Reader reader = null;
|
|
try
|
|
{
|
|
InputStream stream = getClass().getClassLoader().getResourceAsStream(scriptClasspath);
|
|
if (stream == null)
|
|
{
|
|
throw new AlfrescoRuntimeException("Unable to load classpath resource: " + scriptClasspath);
|
|
}
|
|
reader = new InputStreamReader(stream);
|
|
|
|
return executeScriptImpl(reader, model);
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
throw new ScriptException("Failed to execute script '" + scriptClasspath + "': " + err.getMessage(), err);
|
|
}
|
|
finally
|
|
{
|
|
if (reader != null)
|
|
{
|
|
try {reader.close();} catch (IOException ioErr) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.repository.ScriptService#executeScript(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.util.Map)
|
|
*/
|
|
public Object executeScript(NodeRef scriptRef, QName contentProp, Map<String, Object> model)
|
|
throws ScriptException
|
|
{
|
|
if (scriptRef == null)
|
|
{
|
|
throw new IllegalArgumentException("Script NodeRef is mandatory.");
|
|
}
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Executing script: " + scriptRef.toString());
|
|
}
|
|
|
|
Reader reader = null;
|
|
try
|
|
{
|
|
if (this.services.getNodeService().exists(scriptRef) == false)
|
|
{
|
|
throw new AlfrescoRuntimeException("Script Node does not exist: " + scriptRef);
|
|
}
|
|
|
|
if (contentProp == null)
|
|
{
|
|
contentProp = ContentModel.PROP_CONTENT;
|
|
}
|
|
ContentReader cr = this.services.getContentService().getReader(scriptRef, contentProp);
|
|
if (cr == null || cr.exists() == false)
|
|
{
|
|
throw new AlfrescoRuntimeException("Script Node content not found: " + scriptRef);
|
|
}
|
|
reader = new InputStreamReader(cr.getContentInputStream());
|
|
|
|
return executeScriptImpl(reader, model);
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
throw new ScriptException("Failed to execute script '" + scriptRef.toString() + "': " + err.getMessage(), err);
|
|
}
|
|
finally
|
|
{
|
|
if (reader != null)
|
|
{
|
|
try {reader.close();} catch (IOException ioErr) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.repository.ScriptService#executeScript(org.alfresco.service.cmr.repository.ScriptLocation, java.util.Map)
|
|
*/
|
|
public Object executeScript(ScriptLocation location, Map<String, Object> model)
|
|
throws ScriptException
|
|
{
|
|
ParameterCheck.mandatory("Location", location);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Executing script: " + location.toString());
|
|
}
|
|
|
|
Reader reader = null;
|
|
try
|
|
{
|
|
return executeScriptImpl(location.getReader(), model);
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
throw new ScriptException("Failed to execute script '" + location.toString() + "': " + err.getMessage(), err);
|
|
}
|
|
finally
|
|
{
|
|
if (reader != null)
|
|
{
|
|
try {reader.close();} catch (IOException ioErr) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map)
|
|
*/
|
|
public Object executeScriptString(String script, Map<String, Object> model)
|
|
throws ScriptException
|
|
{
|
|
if (script == null || script.length() == 0)
|
|
{
|
|
throw new IllegalArgumentException("Script argument is mandatory.");
|
|
}
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Executing script:\n" + script);
|
|
}
|
|
|
|
Reader reader = null;
|
|
try
|
|
{
|
|
reader = new StringReader(script);
|
|
|
|
return executeScriptImpl(reader, model);
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute the script content from the supplied Reader. Adds the data model into the default
|
|
* root scope for access by the script.
|
|
*
|
|
* @param reader Reader referencing the script to execute.
|
|
* @param model Data model containing objects to be added to the root scope.
|
|
*
|
|
* @return result of the script execution, can be null.
|
|
*
|
|
* @throws AlfrescoRuntimeException
|
|
*/
|
|
private Object executeScriptImpl(Reader reader, Map<String, Object> model)
|
|
throws AlfrescoRuntimeException
|
|
{
|
|
long startTime = 0;
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
startTime = System.currentTimeMillis();
|
|
}
|
|
|
|
// check that rhino script engine is available
|
|
Context cx = Context.enter();
|
|
try
|
|
{
|
|
// The easiest way to embed Rhino is just to create a new scope this way whenever
|
|
// you need one. However, initStandardObjects is an expensive method to call and it
|
|
// allocates a fair amount of memory.
|
|
Scriptable scope = cx.initStandardObjects();
|
|
|
|
// there's always a model, if only to hold the util objects
|
|
if (model == null)
|
|
{
|
|
model = new HashMap<String, Object>();
|
|
}
|
|
|
|
// add the global scripts
|
|
for (ScriptImplementation script : this.globalScripts)
|
|
{
|
|
model.put(script.getScriptName(), script);
|
|
}
|
|
|
|
// insert supplied object model into root of the default scope
|
|
for (String key : model.keySet())
|
|
{
|
|
// set the root scope on appropriate objects
|
|
// this is used to allow native JS object creation etc.
|
|
Object obj = model.get(key);
|
|
if (obj instanceof Scopeable)
|
|
{
|
|
((Scopeable)obj).setScope(scope);
|
|
}
|
|
|
|
// convert/wrap each object to JavaScript compatible
|
|
Object jsObject = Context.javaToJS(obj, scope);
|
|
|
|
// insert into the root scope ready for access by the script
|
|
ScriptableObject.putProperty(scope, key, jsObject);
|
|
}
|
|
|
|
// execute the script
|
|
Object result = cx.evaluateReader(scope, reader, "AlfrescoScript", 1, null);
|
|
|
|
// extract java object result if wrapped by rhinoscript
|
|
if (result instanceof Wrapper)
|
|
{
|
|
result = ((Wrapper)result).unwrap();
|
|
}
|
|
else if (result instanceof NativeArray)
|
|
{
|
|
result = Context.jsToJava(result, Object[].class);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
throw new AlfrescoRuntimeException(err.getMessage(), err);
|
|
}
|
|
finally
|
|
{
|
|
Context.exit();
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
long endTime = System.currentTimeMillis();
|
|
logger.debug("Time to execute script: " + (endTime - startTime) + "ms");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create the default data-model available to scripts as global scope level objects:
|
|
* <p>
|
|
* 'companyhome' - the Company Home node<br>
|
|
* 'userhome' - the current user home space node<br>
|
|
* 'person' - the node representing the current user Person<br>
|
|
* 'script' - the node representing the script itself (may not be available)<br>
|
|
* 'document' - document context node (may not be available)<br>
|
|
* 'space' - space context node (may not be available)
|
|
*
|
|
* @param services ServiceRegistry
|
|
* @param person The current user Person Node
|
|
* @param companyHome The CompanyHome ref
|
|
* @param userHome The User home space ref
|
|
* @param script Optional ref to the script itself
|
|
* @param document Optional ref to a document Node
|
|
* @param space Optional ref to a space Node
|
|
* @param resolver Image resolver to resolve icon images etc.
|
|
*
|
|
* @return A Map of global scope scriptable Node objects
|
|
*/
|
|
public static Map<String, Object> buildDefaultModel(
|
|
ServiceRegistry services,
|
|
NodeRef person, NodeRef companyHome, NodeRef userHome,
|
|
NodeRef script, NodeRef document, NodeRef space)
|
|
{
|
|
Map<String, Object> model = new HashMap<String, Object>();
|
|
|
|
// add the well known node wrapper objects
|
|
model.put("companyhome", new Node(companyHome, services));
|
|
model.put("userhome", new Node(userHome, services));
|
|
model.put("person", new Node(person, services));
|
|
if (script != null)
|
|
{
|
|
model.put("script", new Node(script, services));
|
|
}
|
|
if (document != null)
|
|
{
|
|
model.put("document", new Node(document, services));
|
|
}
|
|
if (space != null)
|
|
{
|
|
model.put("space", new Node(space, services));
|
|
}
|
|
|
|
return model;
|
|
}
|
|
}
|