mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
. Checkpoint for the Rhino JavaScript engine integration:
- Additions to the Alfresco JavaScript data-model . JUnit test for each entry point into Rhino and the ScriptService - tests for various API calls on the Scriptable Node object . More javadoc clean-up in templating and script services git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2726 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -23,6 +23,7 @@ import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
@@ -37,6 +38,7 @@ import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateImageResolver;
|
||||
import org.alfresco.service.cmr.repository.TemplateNode;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -44,11 +46,12 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Node class specific for use by Template pages that support Bean objects as part of the model.
|
||||
* The default template engine FreeMarker can use these objects and they are provided to support it.
|
||||
* A single method is completely freemarker specific - getXmlNodeModel()
|
||||
* Node class implementation specific for use by ScriptService as part of the object model.
|
||||
* <p>
|
||||
* The class exposes Node properties, children as dynamically populated maps and lists.
|
||||
* <p>
|
||||
* The class exposes Node properties, children and assocs as dynamically populated maps and lists.
|
||||
* The various collection classes are mirrored as JavaScript properties. So can be accessed using
|
||||
* standard JavaScript syntax, such as <code>node.children[0].properties.name</code>.
|
||||
* <p>
|
||||
* Various helper methods are provided to access common and useful node variables such
|
||||
* as the content url and type information.
|
||||
@@ -113,8 +116,6 @@ public final class Node implements Serializable
|
||||
this.id = nodeRef.getId();
|
||||
this.services = services;
|
||||
this.imageResolver = resolver;
|
||||
|
||||
this.properties = new ScriptableQNameMap<String, Object>(this.services.getNamespaceService());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,6 +126,11 @@ public final class Node implements Serializable
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String jsGet_id()
|
||||
{
|
||||
return getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the NodeRef this Node object represents
|
||||
*/
|
||||
@@ -133,6 +139,11 @@ public final class Node implements Serializable
|
||||
return this.nodeRef;
|
||||
}
|
||||
|
||||
public NodeRef jsGet_nodeRef()
|
||||
{
|
||||
return getNodeRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the type.
|
||||
*/
|
||||
@@ -146,6 +157,11 @@ public final class Node implements Serializable
|
||||
return type;
|
||||
}
|
||||
|
||||
public QName jsGet_type()
|
||||
{
|
||||
return getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The display name for the node
|
||||
*/
|
||||
@@ -174,6 +190,16 @@ public final class Node implements Serializable
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String jsGet_name()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
|
||||
public void jsSet_name(String name)
|
||||
{
|
||||
this.getProperties().put(ContentModel.PROP_NAME.toString(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The children of this Node as Node wrappers
|
||||
*/
|
||||
@@ -194,21 +220,54 @@ public final class Node implements Serializable
|
||||
return this.children;
|
||||
}
|
||||
|
||||
public Node[] jsGet_children()
|
||||
{
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A map capable of returning the Node at the specified Path as a child of this node.
|
||||
* @return Returns the Node at the specified 'cm:name' based Path walking the children of this Node.
|
||||
* So a valid call might be <code>mynode.childByNamePath("/QA/Testing/Docs");</code>
|
||||
*/
|
||||
//public Map getChildByNamePath()
|
||||
public Node childByNamePath(String path)
|
||||
{
|
||||
// convert the name based path to a valid XPath query
|
||||
StringBuilder xpath = new StringBuilder(path.length() << 1);
|
||||
for (StringTokenizer t = new StringTokenizer(path, "/"); t.hasMoreTokens(); /**/)
|
||||
{
|
||||
if (xpath.length() != 0)
|
||||
{
|
||||
xpath.append('/');
|
||||
}
|
||||
xpath.append("*[@cm:name='")
|
||||
.append(t.nextToken()) // TODO: use QueryParameterDefinition see FileFolderService.search()
|
||||
.append("']");
|
||||
}
|
||||
|
||||
Node[] nodes = getChildrenByXPath(xpath.toString(), true);
|
||||
|
||||
return (nodes.length != 0) ? nodes[0] : null;
|
||||
}
|
||||
|
||||
// TODO: find out why this doesn't work - the function defs do not seem to get found
|
||||
//public Node jsFunction_childByNamePath(String path)
|
||||
//{
|
||||
// return new NamePathResultsMap(this, this.services);
|
||||
// return getChildByNamePath(path);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @return A map capable of returning a List of Node objects from an XPath query
|
||||
* as children of this node.
|
||||
* @return Returns the Nodes at the specified XPath walking the children of this Node.
|
||||
* So a valid call might be <code>mynode.childrenByXPath("/*[@cm:name='Testing']/*");</code>
|
||||
*/
|
||||
//public Map getChildrenByXPath()
|
||||
public Node[] childrenByXPath(String xpath)
|
||||
{
|
||||
return getChildrenByXPath(xpath, false);
|
||||
}
|
||||
|
||||
// TODO: find out why this doesn't work - the function defs do not seem to get found
|
||||
//public Node[] jsFunction_childrenByXPath(String xpath)
|
||||
//{
|
||||
// return new XPathResultsMap(this, this.services);
|
||||
// return childrenByXPath(xpath);
|
||||
//}
|
||||
|
||||
/**
|
||||
@@ -242,8 +301,10 @@ public final class Node implements Serializable
|
||||
{
|
||||
if (this.assocs == null)
|
||||
{
|
||||
List<AssociationRef> refs = this.services.getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL);
|
||||
// this Map implements the Scriptable interface for native JS syntax property access
|
||||
this.assocs = new ScriptableQNameMap<String, Node[]>(this.services.getNamespaceService());
|
||||
|
||||
List<AssociationRef> refs = this.services.getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL);
|
||||
for (AssociationRef ref : refs)
|
||||
{
|
||||
String qname = ref.getTypeQName().toString();
|
||||
@@ -268,6 +329,11 @@ public final class Node implements Serializable
|
||||
return this.assocs;
|
||||
}
|
||||
|
||||
public Map<String, Node[]> jsGet_assocs()
|
||||
{
|
||||
return getAssocs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the properties known about this node.
|
||||
*
|
||||
@@ -281,8 +347,10 @@ public final class Node implements Serializable
|
||||
{
|
||||
if (this.propsRetrieved == false)
|
||||
{
|
||||
Map<QName, Serializable> props = this.services.getNodeService().getProperties(this.nodeRef);
|
||||
// this Map implements the Scriptable interface for native JS syntax property access
|
||||
this.properties = new ScriptableQNameMap<String, Object>(this.services.getNamespaceService());
|
||||
|
||||
Map<QName, Serializable> props = this.services.getNodeService().getProperties(this.nodeRef);
|
||||
for (QName qname : props.keySet())
|
||||
{
|
||||
Serializable propValue = props.get(qname);
|
||||
@@ -307,6 +375,11 @@ public final class Node implements Serializable
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
public Map<String, Object> jsGet_properties()
|
||||
{
|
||||
return getProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this Node is a container (i.e. a folder)
|
||||
*/
|
||||
@@ -322,6 +395,11 @@ public final class Node implements Serializable
|
||||
return isContainer.booleanValue();
|
||||
}
|
||||
|
||||
public boolean jsGet_isContainer()
|
||||
{
|
||||
return isContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this Node is a Document (i.e. with content)
|
||||
*/
|
||||
@@ -336,6 +414,11 @@ public final class Node implements Serializable
|
||||
return isDocument.booleanValue();
|
||||
}
|
||||
|
||||
public boolean jsGet_isDocument()
|
||||
{
|
||||
return isDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The list of aspects applied to this node
|
||||
*/
|
||||
@@ -349,6 +432,11 @@ public final class Node implements Serializable
|
||||
return this.aspects;
|
||||
}
|
||||
|
||||
public QName[] jsGet_aspects()
|
||||
{
|
||||
return getAspects().toArray(new QName[getAspects().size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aspect The aspect name to test for
|
||||
*
|
||||
@@ -400,6 +488,11 @@ public final class Node implements Serializable
|
||||
return displayPath;
|
||||
}
|
||||
|
||||
public String jsGet_displayPath()
|
||||
{
|
||||
return getDisplayPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the small icon image for this node
|
||||
*/
|
||||
@@ -422,6 +515,11 @@ public final class Node implements Serializable
|
||||
}
|
||||
}
|
||||
|
||||
public String jsGet_icon16()
|
||||
{
|
||||
return getIcon16();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the large icon image for this node
|
||||
*/
|
||||
@@ -452,6 +550,11 @@ public final class Node implements Serializable
|
||||
}
|
||||
}
|
||||
|
||||
public String jsGet_icon32()
|
||||
{
|
||||
return getIcon32();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the node is currently locked
|
||||
*/
|
||||
@@ -471,6 +574,11 @@ public final class Node implements Serializable
|
||||
return locked;
|
||||
}
|
||||
|
||||
public boolean jsGet_isLocked()
|
||||
{
|
||||
return isLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the parent node
|
||||
*/
|
||||
@@ -489,6 +597,11 @@ public final class Node implements Serializable
|
||||
return parent;
|
||||
}
|
||||
|
||||
public Node jsGet_parent()
|
||||
{
|
||||
return getParent();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the primary parent association so we can get at the association QName and the association type QName.
|
||||
@@ -502,6 +615,11 @@ public final class Node implements Serializable
|
||||
return primaryParentAssoc;
|
||||
}
|
||||
|
||||
public ChildAssociationRef jsGet_primaryParentAssoc()
|
||||
{
|
||||
return getPrimaryParentAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the content String for this node from the default content property
|
||||
* (@see ContentModel.PROP_CONTENT)
|
||||
@@ -513,6 +631,11 @@ public final class Node implements Serializable
|
||||
return (reader != null && reader.exists()) ? reader.getContentString() : "";
|
||||
}
|
||||
|
||||
public String jsGet_content()
|
||||
{
|
||||
return getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return For a content document, this method returns the URL to the content stream for
|
||||
* the default content property (@see ContentModel.PROP_CONTENT)
|
||||
@@ -545,6 +668,11 @@ public final class Node implements Serializable
|
||||
}
|
||||
}
|
||||
|
||||
public String jsGet_url()
|
||||
{
|
||||
return getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The mimetype encoding for content attached to the node from the default content property
|
||||
* (@see ContentModel.PROP_CONTENT)
|
||||
@@ -563,6 +691,11 @@ public final class Node implements Serializable
|
||||
return mimetype;
|
||||
}
|
||||
|
||||
public String jsGet_mimetype()
|
||||
{
|
||||
return getMimetype();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The size in bytes of the content attached to the node from the default content property
|
||||
* (@see ContentModel.PROP_CONTENT)
|
||||
@@ -581,6 +714,11 @@ public final class Node implements Serializable
|
||||
return size != null ? size.longValue() : 0L;
|
||||
}
|
||||
|
||||
public long jsGet_size()
|
||||
{
|
||||
return getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the image resolver instance used by this node
|
||||
*/
|
||||
@@ -606,6 +744,63 @@ public final class Node implements Serializable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list or a single Node from executing an xpath against the parent Node.
|
||||
*
|
||||
* @param xpath XPath to execute
|
||||
* @param firstOnly True to return the first result only
|
||||
*
|
||||
* @return Node[] can be empty but never null
|
||||
*/
|
||||
private Node[] getChildrenByXPath(String xpath, boolean firstOnly)
|
||||
{
|
||||
Node[] result = null;
|
||||
|
||||
if (xpath.length() != 0)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Executing xpath: " + xpath);
|
||||
|
||||
NodeRef contextRef;
|
||||
if (getParent() != null)
|
||||
{
|
||||
contextRef = getParent().getNodeRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
contextRef = this.services.getNodeService().getRootNode(nodeRef.getStoreRef());
|
||||
}
|
||||
List<NodeRef> nodes = this.services.getSearchService().selectNodes(
|
||||
contextRef,
|
||||
xpath,
|
||||
null,
|
||||
this.services.getNamespaceService(),
|
||||
false);
|
||||
|
||||
// see if we only want the first result
|
||||
if (firstOnly == true)
|
||||
{
|
||||
if (nodes.size() != 0)
|
||||
{
|
||||
result = new Node[1];
|
||||
result[0] = new Node(nodes.get(0), this.services, this.imageResolver);
|
||||
}
|
||||
}
|
||||
// or all the results
|
||||
else
|
||||
{
|
||||
result = new Node[nodes.size()];
|
||||
for (int i=0; i<nodes.size(); i++)
|
||||
{
|
||||
NodeRef ref = nodes.get(i);
|
||||
result[i] = new Node(ref, this.services, this.imageResolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result != null ? result : new Node[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class wrapping and providing access to a ContentData property
|
||||
|
@@ -31,6 +31,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.ScriptException;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
@@ -71,7 +72,8 @@ public class RhinoScriptService implements ScriptService
|
||||
/**
|
||||
* @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
|
||||
public Object executeScript(String scriptClasspath, Map<String, Object> model)
|
||||
throws ScriptException
|
||||
{
|
||||
if (scriptClasspath == null)
|
||||
{
|
||||
@@ -104,9 +106,10 @@ public class RhinoScriptService implements ScriptService
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.service.cmr.repository.ScriptService#executeScript(org.alfresco.service.cmr.repository.NodeRef, java.util.Map)
|
||||
* @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, Map<String, Object> model) throws ScriptException
|
||||
public Object executeScript(NodeRef scriptRef, QName contentProp, Map<String, Object> model)
|
||||
throws ScriptException
|
||||
{
|
||||
if (scriptRef == null)
|
||||
{
|
||||
@@ -120,7 +123,12 @@ public class RhinoScriptService implements ScriptService
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Script Node does not exist: " + scriptRef);
|
||||
}
|
||||
ContentReader cr = this.contentService.getReader(scriptRef, ContentModel.PROP_CONTENT);
|
||||
|
||||
if (contentProp == null)
|
||||
{
|
||||
contentProp = ContentModel.PROP_CONTENT;
|
||||
}
|
||||
ContentReader cr = this.contentService.getReader(scriptRef, contentProp);
|
||||
if (cr == null || cr.exists() == false)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Script Node content not found: " + scriptRef);
|
||||
@@ -145,7 +153,8 @@ public class RhinoScriptService implements ScriptService
|
||||
/**
|
||||
* @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
|
||||
public Object executeScriptString(String script, Map<String, Object> model)
|
||||
throws ScriptException
|
||||
{
|
||||
if (script == null || script.length() == 0)
|
||||
{
|
||||
@@ -193,7 +202,8 @@ public class RhinoScriptService implements ScriptService
|
||||
{
|
||||
for (String key : model.keySet())
|
||||
{
|
||||
ScriptableObject.putProperty(scope, key, model.get(key));
|
||||
Object jsObject = Context.javaToJS(model.get(key), scope);
|
||||
ScriptableObject.putProperty(scope, key, jsObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,10 +17,14 @@
|
||||
package org.alfresco.repo.jscript;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.dictionary.DictionaryComponent;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
@@ -29,11 +33,14 @@ import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||
import org.alfresco.repo.transaction.TransactionUtil;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.mozilla.javascript.Context;
|
||||
@@ -49,11 +56,11 @@ public class RhinoScriptTest extends TestCase
|
||||
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
|
||||
private ContentService contentService;
|
||||
private TemplateService templateService;
|
||||
private NodeService nodeService;
|
||||
private TransactionService transactionService;
|
||||
private ServiceRegistry serviceRegistry;
|
||||
private AuthenticationComponent authenticationComponent;
|
||||
private ScriptService scriptService;
|
||||
|
||||
/*
|
||||
* @see junit.framework.TestCase#setUp()
|
||||
@@ -65,7 +72,7 @@ public class RhinoScriptTest extends TestCase
|
||||
transactionService = (TransactionService)this.ctx.getBean("transactionComponent");
|
||||
contentService = (ContentService)this.ctx.getBean("contentService");
|
||||
nodeService = (NodeService)this.ctx.getBean("nodeService");
|
||||
templateService = (TemplateService)this.ctx.getBean("templateService");
|
||||
scriptService = (ScriptService)this.ctx.getBean("scriptService");
|
||||
serviceRegistry = (ServiceRegistry)this.ctx.getBean("ServiceRegistry");
|
||||
|
||||
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
|
||||
@@ -98,7 +105,55 @@ public class RhinoScriptTest extends TestCase
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testRhino()
|
||||
public void testRhinoIntegration()
|
||||
{
|
||||
TransactionUtil.executeInUserTransaction(
|
||||
transactionService,
|
||||
new TransactionUtil.TransactionWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
// 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();
|
||||
|
||||
// Now we can evaluate a script. Let's create a new associative array object
|
||||
// using the object literal notation to create the members
|
||||
Object result = cx.evaluateString(scope, "obj = {a:1, b:['x','y']}", "TestJS1", 1, null);
|
||||
|
||||
Scriptable obj = (Scriptable)scope.get("obj", scope);
|
||||
|
||||
// Should resolve a non-null value
|
||||
assertNotNull(obj.get("a", obj));
|
||||
|
||||
// should resolve "obj.a == 1" - JavaScript objects come back as Number
|
||||
assertEquals(new Double(1.0), obj.get("a", obj));
|
||||
|
||||
// try another script eval - this time a function call returning a result
|
||||
result = cx.evaluateString(scope, "function f(x) {return x+1} f(7)", "TestJS2", 1, null);
|
||||
assertEquals(8.0, cx.toNumber(result));
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
err.printStackTrace();
|
||||
fail(err.getMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
cx.exit();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testJSObjectWrapping()
|
||||
{
|
||||
TransactionUtil.executeInUserTransaction(
|
||||
transactionService,
|
||||
@@ -110,44 +165,27 @@ public class RhinoScriptTest extends TestCase
|
||||
NodeRef root = nodeService.getRootNode(store);
|
||||
BaseNodeServiceTest.buildNodeGraph(nodeService, root);
|
||||
|
||||
// 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();
|
||||
|
||||
// wrap System.out so we can perform println() from within scripts
|
||||
Object wrappedOut = Context.javaToJS(System.out, scope);
|
||||
ScriptableObject.putProperty(scope, "out", wrappedOut);
|
||||
|
||||
// Now we can evaluate a script. Let's create a new associative array object
|
||||
// using the object literal notation to create the members
|
||||
Object result = cx.evaluateString(scope, "obj = {a:1, b:['x','y']}", "TestJS1", 1, null);
|
||||
|
||||
Scriptable obj = (Scriptable)scope.get("obj", scope);
|
||||
|
||||
// Should resolve a non-null value
|
||||
assertNotNull(obj.get("a", obj));
|
||||
// should resolve "obj.a == 1" - JavaScript objects come back as Number
|
||||
assertEquals(new Double(1.0), obj.get("a", obj));
|
||||
|
||||
// try another script eval
|
||||
result = cx.evaluateString(scope, "function f(x) {return x+1} f(7)", "TestJS2", 1, null);
|
||||
assertEquals(8.0, cx.toNumber(result));
|
||||
|
||||
// wrap a simple Alfresco NodeRef object
|
||||
// wrap a simple NodeRef Java object
|
||||
// we can use Java style method calls within the script to access it's properties
|
||||
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(root);
|
||||
NodeRef ref1 = childAssocs.get(0).getChildRef();
|
||||
Object wrappedNodeRef = Context.javaToJS(ref1, scope);
|
||||
ScriptableObject.putProperty(scope, "rootref", wrappedNodeRef);
|
||||
|
||||
// evaluate script that touches the wrapped NodeRef
|
||||
result = cx.evaluateString(scope, "obj = rootref.getId()", "TestJS3", 1, null);
|
||||
Object result = cx.evaluateString(scope, "obj = rootref.getId()", "TestJS3", 1, null);
|
||||
assertEquals(ref1.getId(), cx.toString(result));
|
||||
|
||||
// wrap a script Alfresco Node object - the Node object is a wrapper like TemplateNode
|
||||
// wrap a scriptable Alfresco Node object - the Node object is a wrapper like TemplateNode
|
||||
Node node1 = new Node(root, serviceRegistry, null);
|
||||
Object wrappedNode = Context.javaToJS(node1, scope);
|
||||
ScriptableObject.putProperty(scope, "root", wrappedNode);
|
||||
@@ -170,15 +208,79 @@ public class RhinoScriptTest extends TestCase
|
||||
});
|
||||
}
|
||||
|
||||
//private static final String TEMPLATE_1 = "org/alfresco/repo/template/test_template1.ftl";
|
||||
public void testScriptService()
|
||||
{
|
||||
TransactionUtil.executeInUserTransaction(
|
||||
transactionService,
|
||||
new TransactionUtil.TransactionWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
StoreRef store = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "rhino_" + System.currentTimeMillis());
|
||||
NodeRef root = nodeService.getRootNode(store);
|
||||
BaseNodeServiceTest.buildNodeGraph(nodeService, root);
|
||||
|
||||
try
|
||||
{
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("out", System.out);
|
||||
|
||||
// create an Alfresco scriptable Node object
|
||||
// the Node object is a wrapper similar to the TemplateNode concept
|
||||
Node rootNode = new Node(root, serviceRegistry, null);
|
||||
model.put("root", rootNode);
|
||||
|
||||
// execute test scripts using the various entry points of the ScriptService
|
||||
|
||||
// test executing a script file via classpath lookup
|
||||
Object result = scriptService.executeScript(TESTSCRIPT_CLASSPATH1, model);
|
||||
System.out.println("Result from TESTSCRIPT_CLASSPATH1: " + result.toString());
|
||||
assertTrue((Boolean)result); // we know the result is a boolean
|
||||
|
||||
// test executing a script embedded inside Node content
|
||||
ChildAssociationRef childRef = nodeService.createNode(
|
||||
root,
|
||||
BaseNodeServiceTest.ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName(BaseNodeServiceTest.NAMESPACE, "script_content"),
|
||||
BaseNodeServiceTest.TYPE_QNAME_TEST_CONTENT,
|
||||
null);
|
||||
NodeRef contentNodeRef = childRef.getChildRef();
|
||||
ContentWriter writer = contentService.getWriter(
|
||||
contentNodeRef,
|
||||
BaseNodeServiceTest.PROP_QNAME_TEST_CONTENT,
|
||||
true);
|
||||
writer.setMimetype("application/x-javascript");
|
||||
writer.putContent(TESTSCRIPT1);
|
||||
scriptService.executeScript(contentNodeRef, BaseNodeServiceTest.PROP_QNAME_TEST_CONTENT, model);
|
||||
|
||||
// test executing a script directly as a String
|
||||
scriptService.executeScriptString(TESTSCRIPT1, model);
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
err.printStackTrace();
|
||||
fail(err.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final String TESTSCRIPT_CLASSPATH1 = "org/alfresco/repo/jscript/test_script1.js";
|
||||
|
||||
private static final String TESTSCRIPT1 =
|
||||
"var id = root.getId();\r\n" +
|
||||
"var id = root.id;\r\n" +
|
||||
"out.println(id);\r\n" +
|
||||
"var name = root.getName();\r\n" +
|
||||
"var name = root.name;\r\n" +
|
||||
"out.println(name);\r\n" +
|
||||
"var type = root.getType();\r\n" +
|
||||
"var type = root.type;\r\n" +
|
||||
"out.println(type);\r\n" +
|
||||
"var childList = root.getChildren();\r\n" +
|
||||
"out.println(\"zero index node: \" + childList[0].getName());\r\n" +
|
||||
"out.println(\"properties: \" + childList[0].getProperties()[\"name\"] );";
|
||||
"var childList = root.children;\r\n" +
|
||||
"out.println(\"zero index node name: \" + childList[0].name);\r\n" +
|
||||
"out.println(\"name property access: \" + childList[0].properties.name );\r\n" +
|
||||
"var childByNameNode = root.childByNamePath(\"/\" + childList[0].name);\r\n" +
|
||||
"out.println(\"child by name path: \" + childByNameNode.name);\r\n" +
|
||||
"var xpathResults = root.childrenByXPath(\"/*\");\r\n" +
|
||||
"out.println(\"children of root from xpath: \" + xpathResults.length);\r\n";
|
||||
}
|
||||
|
@@ -40,14 +40,21 @@ public class ScriptableQNameMap<K,V> extends QNameMap<K,V> implements Scriptable
|
||||
{
|
||||
return "ScriptableQNameMap";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.mozilla.javascript.Scriptable#get(java.lang.String, org.mozilla.javascript.Scriptable)
|
||||
*/
|
||||
public Object get(String name, Scriptable start)
|
||||
{
|
||||
// get the property from the underlying QName map
|
||||
return get(name);
|
||||
if ("length".equals(name))
|
||||
{
|
||||
return this.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return get(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
25
source/java/org/alfresco/repo/jscript/test_script1.js
Normal file
25
source/java/org/alfresco/repo/jscript/test_script1.js
Normal file
@@ -0,0 +1,25 @@
|
||||
var id = root.id;
|
||||
var name = root.name;
|
||||
out.println("Name: " + name);
|
||||
var type = root.type;
|
||||
out.println("ID: " + id + " of type: " + type);
|
||||
var noderef = root.nodeRef;
|
||||
out.println("NodeRef: " + noderef);
|
||||
var childList = root.children;
|
||||
out.println("Has " + childList.length + " child nodes");
|
||||
var properties = root.properties;
|
||||
out.println("Property Count: " + properties.length);
|
||||
var assocs = root.assocs;
|
||||
out.println("Assoc Count: " + assocs.length);
|
||||
|
||||
// test various access mechanisms
|
||||
var childname1 = childList[0].name;
|
||||
var childname2 = childList[0].properties.name
|
||||
var childname3 = childList[0].properties["name"];
|
||||
var childname4 = childList[0].properties["cm:name"];
|
||||
|
||||
function result()
|
||||
{
|
||||
return (childname1 == childname2 && childname2 == childname3 && childname3 == childname4);
|
||||
}
|
||||
result();
|
Reference in New Issue
Block a user