Derek Hulley 72bb79696d Merged 1.4 to HEAD
svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4252 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4294 .
   svn revert root\common\common.xml
   svn resolved root\projects\repository\config\alfresco\script-services-context.xml


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4634 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2006-12-18 13:45:11 +00:00

399 lines
14 KiB
Java

/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.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.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.ScriptService;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
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.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>(5);
/**
* 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();
}
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>
* '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
*
* @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)
{
return buildDefaultModel(services, person, companyHome, userHome, script, document, space, null);
}
/**
* 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,
TemplateImageResolver resolver)
{
Map<String, Object> model = new HashMap<String, Object>();
// add the well known node wrapper objects
model.put("companyhome", new Node(companyHome, services, resolver));
model.put("userhome", new Node(userHome, services, resolver));
model.put("person", new Node(person, services, resolver));
if (script != null)
{
model.put("script", new Node(script, services, resolver));
}
if (document != null)
{
model.put("document", new Node(document, services, resolver));
}
if (space != null)
{
model.put("space", new Node(space, services, resolver));
}
model.put("search", new Search(services, companyHome.getStoreRef(), resolver));
model.put("session", new Session(services, resolver));
model.put("classification", new Classification(services, companyHome.getStoreRef(), resolver));
return model;
}
}