mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-02 17:35:18 +00:00
were both beyond me, and are just the raw conflict merge data. If Kev can't figure out how they should go together by tomorrow AM (for me) I'll dig back in. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4306 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
366 lines
13 KiB
Java
366 lines
13 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.ScriptService;
|
|
import org.alfresco.service.cmr.repository.TemplateImageResolver;
|
|
import org.alfresco.service.namespace.QName;
|
|
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#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;
|
|
}
|
|
}
|