Refactor of template and script services to allow easy addition of further template and script processors.

Hightlights of check-in include:
- Introduction of script processor 
- Neutralisation of script and template models
- The notion of a processor extension introduced
- Extensions applied to processor implementation rather than the services
- Auto selection of processor based on file extension of template or script

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5519 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2007-04-20 17:13:34 +00:00
parent 03c56f4c90
commit 5401499003
44 changed files with 1392 additions and 713 deletions

View File

@@ -34,7 +34,7 @@ import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
*
* @author Kevin Roast
*/
public final class AVM extends BaseScopableScriptImplementation
public final class AVM extends BaseScopableProcessorExtension
{
/** Repository Service Registry */
private ServiceRegistry services;

View File

@@ -36,7 +36,7 @@ import org.alfresco.service.cmr.action.ActionService;
*
* @author davidc
*/
public final class Actions extends BaseScopableScriptImplementation
public final class Actions extends BaseScopableProcessorExtension
{
/** Repository Service Registry */
private ServiceRegistry services;

View File

@@ -16,6 +16,7 @@
*/
package org.alfresco.repo.jscript;
import org.alfresco.repo.processor.BaseProcessorExtension;
import org.mozilla.javascript.Scriptable;
/**
@@ -25,7 +26,7 @@ import org.mozilla.javascript.Scriptable;
*
* @author Kevin Roast
*/
public class BaseScopableScriptImplementation extends BaseScriptImplementation implements Scopeable
public class BaseScopableProcessorExtension extends BaseProcessorExtension implements Scopeable
{
private static ThreadLocal<Scriptable> scope = new ThreadLocal<Scriptable>();
@@ -36,7 +37,7 @@ public class BaseScopableScriptImplementation extends BaseScriptImplementation i
*/
public void setScope(Scriptable scope)
{
this.scope.set(scope);
BaseScopableProcessorExtension.scope.set(scope);
}
/**
@@ -44,6 +45,6 @@ public class BaseScopableScriptImplementation extends BaseScriptImplementation i
*/
public Scriptable getScope()
{
return this.scope.get();
return BaseScopableProcessorExtension.scope.get();
}
}

View File

@@ -1,78 +0,0 @@
/*
* 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 org.alfresco.service.cmr.repository.ScriptImplementation;
import org.alfresco.service.cmr.repository.ScriptService;
/**
* Abstract base class for a script implementation
*
* @author Roy Wetherall
*/
public abstract class BaseScriptImplementation implements ScriptImplementation
{
/** The script service */
private ScriptService scriptService;
/** The name of the script */
private String scriptName;
/**
* Sets the script service
*
* @param scriptService the script service
*/
public void setScriptService(ScriptService scriptService)
{
this.scriptService = scriptService;
}
/**
* Registers this script with the script service
*/
public void register()
{
this.scriptService.registerScript(this);
}
/**
* Sets the script name
*
* @param scriptName the script name
*/
public void setScriptName(String scriptName)
{
this.scriptName = scriptName;
}
/**
* @see org.alfresco.service.cmr.repository.ScriptImplementation#getScriptName()
*/
public String getScriptName()
{
return this.scriptName;
}
}

View File

@@ -37,7 +37,7 @@ import org.alfresco.service.namespace.QName;
*
* @author Andy Hind
*/
public final class Classification extends BaseScopableScriptImplementation
public final class Classification extends BaseScopableProcessorExtension
{
private ServiceRegistry services;

View File

@@ -37,7 +37,7 @@ import org.mozilla.javascript.Scriptable;
*
* @author Kevin Roast
*/
public final class CrossRepositoryCopy extends BaseScopableScriptImplementation
public final class CrossRepositoryCopy extends BaseScopableProcessorExtension
{
public final static String BEAN_NAME = "crossCopyScript";

View File

@@ -39,8 +39,6 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.TransformActionExecuter;
import org.alfresco.repo.content.transform.magick.ImageMagickContentTransformer;
import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.repo.template.FreeMarkerProcessor;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
@@ -1739,8 +1737,7 @@ public class Node implements Serializable, Scopeable
private String processTemplate(String template, NodeRef templateRef, ScriptableObject args)
{
// build default model for the template processing
Map<String, Object> model = FreeMarkerProcessor.buildDefaultModel(
services,
Map<String, Object> model = this.services.getTemplateService().buildDefaultModel(
((Node)((Wrapper)scope.get("person", scope)).unwrap()).getNodeRef(),
((Node)((Wrapper)scope.get("companyhome", scope)).unwrap()).getNodeRef(),
((Node)((Wrapper)scope.get("userhome", scope)).unwrap()).getNodeRef(),
@@ -1750,12 +1747,12 @@ public class Node implements Serializable, Scopeable
// add the current node as either the document/space as appropriate
if (this.getIsDocument())
{
model.put("document", new TemplateNode(this.nodeRef, this.services, null));
model.put("space", new TemplateNode(getPrimaryParentAssoc().getParentRef(), this.services, null));
model.put("document", this.nodeRef);
model.put("space", getPrimaryParentAssoc().getParentRef());
}
else
{
model.put("space", new TemplateNode(this.nodeRef, this.services, null));
model.put("space", this.nodeRef);
}
// add the supplied args to the 'args' root object

View File

@@ -40,7 +40,7 @@ import org.alfresco.util.ParameterCheck;
*
* @author davidc
*/
public final class People extends BaseScopableScriptImplementation
public final class People extends BaseScopableProcessorExtension
{
/** Repository Service Registry */
private ServiceRegistry services;

View File

@@ -36,19 +36,18 @@ import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.repo.processor.BaseProcessor;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.ProcessorExtension;
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.ScriptProcessor;
import org.alfresco.service.cmr.repository.StoreRef;
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.NativeArray;
@@ -58,41 +57,25 @@ import org.mozilla.javascript.Wrapper;
import org.springframework.util.FileCopyUtils;
/**
* Implementation of the ScriptService using the Rhino JavaScript engine.
* Implementation of the ScriptEninge using the Rhino JavaScript engine.
*
* @author Kevin Roast
*/
public class RhinoScriptService implements ScriptService
public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcessor
{
private static final Logger logger = Logger.getLogger(RhinoScriptService.class);
private static final Logger logger = Logger.getLogger(RhinoScriptProcessor.class);
private static final String IMPORT_PREFIX = "<import";
private static final String IMPORT_RESOURCE = "resource=\"";
private static final String PATH_CLASSPATH = "classpath:";
private static final String SCRIPT_ROOT = "_root";
/** Repository Service Registry */
private ServiceRegistry services;
/** Store into which to resolve cm:name based script paths */
private StoreRef storeRef;
/** Store root path to resolve cm:name based scripts path from */
private String storePath;
/** 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;
}
/**
* Set the default store reference
*
@@ -112,102 +95,10 @@ public class RhinoScriptService implements ScriptService
}
/**
* @see org.alfresco.service.cmr.repository.ScriptService#registerScript(java.lang.Object)
* @see org.alfresco.service.cmr.repository.ScriptProcessor#execute(org.alfresco.service.cmr.repository.ScriptLocation, java.util.Map)
*/
public void registerScript(ScriptImplementation script)
public Object execute(ScriptLocation location, Map<String, Object> model)
{
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);
}
try
{
InputStream stream = getClass().getClassLoader().getResourceAsStream(scriptClasspath);
if (stream == null)
{
throw new AlfrescoRuntimeException("Unable to load classpath resource: " + scriptClasspath);
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
FileCopyUtils.copy(stream, os); // both streams are closed
byte[] bytes = os.toByteArray();
return executeScriptImpl(resolveScriptImports(new String(bytes)), model);
}
catch (Throwable err)
{
throw new ScriptException("Failed to execute script '" + scriptClasspath + "': " + err.getMessage(), err);
}
}
/**
* @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());
}
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);
}
return executeScriptImpl(resolveScriptImports(cr.getContentString()), model);
}
catch (Throwable err)
{
throw new ScriptException("Failed to execute script '" + scriptRef.toString() + "': " + err.getMessage(), err);
}
}
/**
* @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());
}
try
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
@@ -221,23 +112,66 @@ public class RhinoScriptService implements ScriptService
throw new ScriptException("Failed to execute script '" + location.toString() + "': " + err.getMessage(), err);
}
}
/**
* @see org.alfresco.service.cmr.repository.ScriptProcessor#execute(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.util.Map)
*/
public Object execute(NodeRef nodeRef, QName contentProp, Map<String, Object> model)
{
try
{
if (this.services.getNodeService().exists(nodeRef) == false)
{
throw new AlfrescoRuntimeException("Script Node does not exist: " + nodeRef);
}
if (contentProp == null)
{
contentProp = ContentModel.PROP_CONTENT;
}
ContentReader cr = this.services.getContentService().getReader(nodeRef, contentProp);
if (cr == null || cr.exists() == false)
{
throw new AlfrescoRuntimeException("Script Node content not found: " + nodeRef);
}
return executeScriptImpl(resolveScriptImports(cr.getContentString()), model);
}
catch (Throwable err)
{
throw new ScriptException("Failed to execute script '" + nodeRef.toString() + "': " + err.getMessage(), err);
}
}
/**
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map)
* @see org.alfresco.service.cmr.repository.ScriptProcessor#execute(java.lang.String, java.util.Map)
*/
public Object executeScriptString(String script, Map<String, Object> model)
throws ScriptException
public Object execute(String location, Map<String, Object> model)
{
try
{
InputStream stream = getClass().getClassLoader().getResourceAsStream(location);
if (stream == null)
{
throw new AlfrescoRuntimeException("Unable to load classpath resource: " + location);
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
FileCopyUtils.copy(stream, os); // both streams are closed
byte[] bytes = os.toByteArray();
return executeScriptImpl(resolveScriptImports(new String(bytes)), model);
}
catch (Throwable err)
{
throw new ScriptException("Failed to execute script '" + location + "': " + err.getMessage(), err);
}
}
/**
* @see org.alfresco.service.cmr.repository.ScriptProcessor#executeString(java.lang.String, java.util.Map)
*/
public Object executeString(String script, Map<String, Object> model)
{
if (script == null || script.length() == 0)
{
throw new IllegalArgumentException("Script argument is mandatory.");
}
if (logger.isDebugEnabled())
{
logger.debug("Executing script:\n" + script);
}
try
{
return executeScriptImpl(resolveScriptImports(script), model);
@@ -247,6 +181,7 @@ public class RhinoScriptService implements ScriptService
throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err);
}
}
/**
* Resolve the imports in the specified script. Include directives are of the following form:
@@ -453,7 +388,7 @@ public class RhinoScriptService implements ScriptService
if (resource.startsWith("/"))
{
// resolve from default SpacesStore as cm:name based path
// we have to assume "/Company Home" as the root for now
// TODO: remove this once FFS correctly allows name path resolving from store root!
NodeRef rootNodeRef = this.services.getNodeService().getRootNode(this.storeRef);
List<NodeRef> nodes = this.services.getSearchService().selectNodes(
rootNodeRef, this.storePath, null, this.services.getNamespaceService(), false);
@@ -516,7 +451,7 @@ public class RhinoScriptService implements ScriptService
*
* @throws AlfrescoRuntimeException
*/
private Object executeScriptImpl(String script, Map<String, Object> model)
private Object executeScriptImpl(String script, Map<String, Object> origModel)
throws AlfrescoRuntimeException
{
long startTime = 0;
@@ -525,6 +460,9 @@ public class RhinoScriptService implements ScriptService
startTime = System.currentTimeMillis();
}
// Convert the model
Map<String, Object> model = convertToRhinoModel(origModel);
// check that rhino script engine is available
Context cx = Context.enter();
try
@@ -541,9 +479,9 @@ public class RhinoScriptService implements ScriptService
}
// add the global scripts
for (ScriptImplementation ex : this.globalScripts)
for (ProcessorExtension ex : this.processExtensions.values())
{
model.put(ex.getScriptName(), ex);
model.put(ex.getExtensionName(), ex);
}
// insert supplied object model into root of the default scope
@@ -596,50 +534,25 @@ public class RhinoScriptService implements ScriptService
}
/**
* 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)
* Converts the passed model into a Rhino model
*
* @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
* @param model the model
* @return Map<String, Object> the converted model
*/
public static Map<String, Object> buildDefaultModel(
ServiceRegistry services,
NodeRef person, NodeRef companyHome, NodeRef userHome,
NodeRef script, NodeRef document, NodeRef space)
private Map<String, Object> convertToRhinoModel(Map<String, Object> model)
{
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)
Map<String, Object> newModel = new HashMap<String, Object>(model.size());
for (Map.Entry<String, Object> entry : model.entrySet())
{
model.put("script", new Node(script, services));
if (entry.getValue() instanceof NodeRef)
{
newModel.put(entry.getKey(), new Node((NodeRef)entry.getValue(), this.services));
}
else
{
newModel.put(entry.getKey(), entry.getValue());
}
}
if (document != null)
{
model.put("document", new Node(document, services));
}
if (space != null)
{
model.put("space", new Node(space, services));
}
return model;
return newModel;
}
}

View File

@@ -260,7 +260,7 @@ public class RhinoScriptTest extends TestCase
scriptService.executeScript(contentNodeRef, BaseNodeServiceTest.PROP_QNAME_TEST_CONTENT, model);
// test executing a script directly as a String
scriptService.executeScriptString(TESTSCRIPT1, model);
scriptService.executeScriptString("javascript", TESTSCRIPT1, model);
}
catch (Throwable err)
{

View File

@@ -24,12 +24,13 @@
*/
package org.alfresco.repo.jscript;
import org.alfresco.repo.processor.BaseProcessorExtension;
import org.apache.log4j.Logger;
/**
* @author Kevin Roast
*/
public final class ScriptLogger extends BaseScriptImplementation
public final class ScriptLogger extends BaseProcessorExtension
{
private static final Logger logger = Logger.getLogger(ScriptLogger.class);

View File

@@ -32,7 +32,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
*
* @author Kevin Roast
*/
public final class ScriptUtils extends BaseScopableScriptImplementation
public final class ScriptUtils extends BaseScopableProcessorExtension
{
/** Services */
private ServiceRegistry services;

View File

@@ -54,7 +54,7 @@ import org.dom4j.io.SAXReader;
*
* @author Kevin Roast
*/
public final class Search extends BaseScopableScriptImplementation
public final class Search extends BaseScopableProcessorExtension
{
/** Service registry */
private ServiceRegistry services;

View File

@@ -24,6 +24,7 @@
*/
package org.alfresco.repo.jscript;
import org.alfresco.repo.processor.BaseProcessorExtension;
import org.alfresco.service.ServiceRegistry;
/**
@@ -33,7 +34,7 @@ import org.alfresco.service.ServiceRegistry;
*
* @author Andy Hind
*/
public class Session extends BaseScriptImplementation
public class Session extends BaseProcessorExtension
{
/** Service registry */
private ServiceRegistry services;