From 5401499003ee28796f4dec994146ce8a12976e73 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Fri, 20 Apr 2007 17:13:34 +0000 Subject: [PATCH] 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 --- config/alfresco/script-services-context.xml | 62 ++- config/alfresco/template-services-context.xml | 51 +-- .../action/executer/ScriptActionExecuter.java | 4 +- .../scheduled/TemplateActionModelFactory.java | 6 +- .../java/org/alfresco/repo/jscript/AVM.java | 2 +- .../org/alfresco/repo/jscript/Actions.java | 2 +- ...va => BaseScopableProcessorExtension.java} | 7 +- .../alfresco/repo/jscript/Classification.java | 2 +- .../repo/jscript/CrossRepositoryCopy.java | 2 +- .../java/org/alfresco/repo/jscript/Node.java | 11 +- .../org/alfresco/repo/jscript/People.java | 2 +- ...Service.java => RhinoScriptProcessor.java} | 261 ++++-------- .../repo/jscript/RhinoScriptTest.java | 2 +- .../alfresco/repo/jscript/ScriptLogger.java | 3 +- .../alfresco/repo/jscript/ScriptUtils.java | 2 +- .../org/alfresco/repo/jscript/Search.java | 2 +- .../org/alfresco/repo/jscript/Session.java | 3 +- .../tool/default-file-mapping.properties | 3 +- .../repo/processor/BaseProcessor.java | 158 +++++++ .../BaseProcessorExtension.java} | 42 +- .../repo/processor/ScriptServiceImpl.java | 403 ++++++++++++++++++ .../repo/processor/TemplateServiceImpl.java | 262 ++++++++++++ .../java/org/alfresco/repo/template/AVM.java | 2 +- ...va => BaseTemplateProcessorExtension.java} | 53 +-- .../repo/template/Classification.java | 2 +- .../repo/template/DateCompareMethod.java | 2 +- .../repo/template/DateIncrementMethod.java | 2 +- .../repo/template/FreeMarkerProcessor.java | 137 +++--- .../repo/template/HasAspectMethod.java | 2 +- .../repo/template/HasPermissionMethod.java | 2 +- .../repo/template/I18NMessageMethod.java | 2 +- .../template/ISO8601DateFormatMethod.java | 2 +- .../org/alfresco/repo/template/Session.java | 2 +- .../repo/template/TemplateServiceImpl.java | 271 ------------ .../repo/template/UrlEncodeMethod.java | 2 +- .../service/cmr/repository/Processor.java | 55 +++ ...mentation.java => ProcessorExtension.java} | 8 +- .../cmr/repository/ScriptException.java | 2 + .../cmr/repository/ScriptProcessor.java | 75 ++++ .../service/cmr/repository/ScriptService.java | 107 ++++- .../cmr/repository/TemplateException.java | 2 + .../cmr/repository/TemplateProcessor.java | 4 +- ...n.java => TemplateProcessorExtension.java} | 11 +- .../cmr/repository/TemplateService.java | 68 ++- 44 files changed, 1392 insertions(+), 713 deletions(-) rename source/java/org/alfresco/repo/jscript/{BaseScopableScriptImplementation.java => BaseScopableProcessorExtension.java} (81%) rename source/java/org/alfresco/repo/jscript/{RhinoScriptService.java => RhinoScriptProcessor.java} (78%) create mode 100644 source/java/org/alfresco/repo/processor/BaseProcessor.java rename source/java/org/alfresco/repo/{jscript/BaseScriptImplementation.java => processor/BaseProcessorExtension.java} (60%) create mode 100644 source/java/org/alfresco/repo/processor/ScriptServiceImpl.java create mode 100644 source/java/org/alfresco/repo/processor/TemplateServiceImpl.java rename source/java/org/alfresco/repo/template/{BaseTemplateExtensionImplementation.java => BaseTemplateProcessorExtension.java} (55%) delete mode 100644 source/java/org/alfresco/repo/template/TemplateServiceImpl.java create mode 100644 source/java/org/alfresco/service/cmr/repository/Processor.java rename source/java/org/alfresco/service/cmr/repository/{ScriptImplementation.java => ProcessorExtension.java} (90%) create mode 100644 source/java/org/alfresco/service/cmr/repository/ScriptProcessor.java rename source/java/org/alfresco/service/cmr/repository/{TemplateExtensionImplementation.java => TemplateProcessorExtension.java} (89%) diff --git a/config/alfresco/script-services-context.xml b/config/alfresco/script-services-context.xml index e6498fc275..082d2ddc14 100644 --- a/config/alfresco/script-services-context.xml +++ b/config/alfresco/script-services-context.xml @@ -3,7 +3,25 @@ - + + + javascript + + + + + + + + + javascript + + + js + + + + @@ -20,20 +38,20 @@ - - - + + + - - + + logger - - + + utils @@ -41,8 +59,8 @@ - - + + actions @@ -50,8 +68,8 @@ - - + + search @@ -62,8 +80,8 @@ - - + + classification @@ -74,8 +92,8 @@ - - + + people @@ -89,8 +107,8 @@ - - + + session @@ -104,8 +122,8 @@ - - + + avm @@ -113,8 +131,8 @@ - - + + crossRepoCopy diff --git a/config/alfresco/template-services-context.xml b/config/alfresco/template-services-context.xml index 291b12e42a..bc287173d5 100644 --- a/config/alfresco/template-services-context.xml +++ b/config/alfresco/template-services-context.xml @@ -3,44 +3,39 @@ - - - - - - - - - freeMarkerProcessor - - - + freemarker - - - - - - + - - - - + + + + + + + + + + + + + + + freemarker + + + ftl + - - + + diff --git a/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java index e5eedd4ef4..17bff269a6 100644 --- a/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java @@ -29,7 +29,6 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ParameterDefinitionImpl; -import org.alfresco.repo.jscript.RhinoScriptService; import org.alfresco.repo.jscript.ScriptAction; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.Action; @@ -129,8 +128,7 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase // the default scripting model provides access to well known objects and searching // facilities - it also provides basic create/update/delete/copy/move services - Map model = RhinoScriptService.buildDefaultModel( - this.serviceRegistry, + Map model = this.serviceRegistry.getScriptService().buildDefaultModel( personRef, getCompanyHome(), homeSpaceRef, diff --git a/source/java/org/alfresco/repo/action/scheduled/TemplateActionModelFactory.java b/source/java/org/alfresco/repo/action/scheduled/TemplateActionModelFactory.java index 0172e0dc7c..a908f9d326 100644 --- a/source/java/org/alfresco/repo/action/scheduled/TemplateActionModelFactory.java +++ b/source/java/org/alfresco/repo/action/scheduled/TemplateActionModelFactory.java @@ -24,6 +24,8 @@ */ package org.alfresco.repo.action.scheduled; +import java.util.Map; + import org.alfresco.service.cmr.repository.NodeRef; /** @@ -46,7 +48,7 @@ public interface TemplateActionModelFactory * * @return - the model for the template engine. */ - public Object getModel(); + public Map getModel(); /** * Build a model with a default node context. @@ -54,5 +56,5 @@ public interface TemplateActionModelFactory * @param nodeRef * @return - the model (with nodeRef as its context). */ - public Object getModel(NodeRef nodeRef); + public Map getModel(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/repo/jscript/AVM.java b/source/java/org/alfresco/repo/jscript/AVM.java index b74fbf7db5..61c37c0aec 100644 --- a/source/java/org/alfresco/repo/jscript/AVM.java +++ b/source/java/org/alfresco/repo/jscript/AVM.java @@ -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; diff --git a/source/java/org/alfresco/repo/jscript/Actions.java b/source/java/org/alfresco/repo/jscript/Actions.java index 9e7f6bb87e..e5574472b0 100644 --- a/source/java/org/alfresco/repo/jscript/Actions.java +++ b/source/java/org/alfresco/repo/jscript/Actions.java @@ -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; diff --git a/source/java/org/alfresco/repo/jscript/BaseScopableScriptImplementation.java b/source/java/org/alfresco/repo/jscript/BaseScopableProcessorExtension.java similarity index 81% rename from source/java/org/alfresco/repo/jscript/BaseScopableScriptImplementation.java rename to source/java/org/alfresco/repo/jscript/BaseScopableProcessorExtension.java index e61fedf813..f2b67436e7 100644 --- a/source/java/org/alfresco/repo/jscript/BaseScopableScriptImplementation.java +++ b/source/java/org/alfresco/repo/jscript/BaseScopableProcessorExtension.java @@ -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 scope = new ThreadLocal(); @@ -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(); } } diff --git a/source/java/org/alfresco/repo/jscript/Classification.java b/source/java/org/alfresco/repo/jscript/Classification.java index 5b98d09ec9..116f02103a 100644 --- a/source/java/org/alfresco/repo/jscript/Classification.java +++ b/source/java/org/alfresco/repo/jscript/Classification.java @@ -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; diff --git a/source/java/org/alfresco/repo/jscript/CrossRepositoryCopy.java b/source/java/org/alfresco/repo/jscript/CrossRepositoryCopy.java index 4fcbec1ca1..9e1fd327a3 100644 --- a/source/java/org/alfresco/repo/jscript/CrossRepositoryCopy.java +++ b/source/java/org/alfresco/repo/jscript/CrossRepositoryCopy.java @@ -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"; diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java index c29cb17c08..e1d439497a 100644 --- a/source/java/org/alfresco/repo/jscript/Node.java +++ b/source/java/org/alfresco/repo/jscript/Node.java @@ -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 model = FreeMarkerProcessor.buildDefaultModel( - services, + Map 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 diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java index e4d8dd007c..d6d74eac3a 100644 --- a/source/java/org/alfresco/repo/jscript/People.java +++ b/source/java/org/alfresco/repo/jscript/People.java @@ -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; diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java b/source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java similarity index 78% rename from source/java/org/alfresco/repo/jscript/RhinoScriptService.java rename to source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java index 8f706fdba4..ba6eca4b8a 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java @@ -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 = " globalScripts = new ArrayList(); - - /** - * 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 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 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 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 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 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 model) - throws ScriptException + public Object execute(String location, Map 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 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 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 model) + private Object executeScriptImpl(String script, Map origModel) throws AlfrescoRuntimeException { long startTime = 0; @@ -525,6 +460,9 @@ public class RhinoScriptService implements ScriptService startTime = System.currentTimeMillis(); } + // Convert the model + Map 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: - *

- * 'companyhome' - the Company Home node
- * 'userhome' - the current user home space node
- * 'person' - the node representing the current user Person
- * 'script' - the node representing the script itself (may not be available)
- * 'document' - document context node (may not be available)
- * '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 the converted model */ - public static Map buildDefaultModel( - ServiceRegistry services, - NodeRef person, NodeRef companyHome, NodeRef userHome, - NodeRef script, NodeRef document, NodeRef space) + private Map convertToRhinoModel(Map model) { - Map model = new HashMap(); - - // 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 newModel = new HashMap(model.size()); + for (Map.Entry 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; } } diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java b/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java index fa20226d17..8befe30559 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java @@ -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) { diff --git a/source/java/org/alfresco/repo/jscript/ScriptLogger.java b/source/java/org/alfresco/repo/jscript/ScriptLogger.java index febd66cbbf..1f55741125 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptLogger.java +++ b/source/java/org/alfresco/repo/jscript/ScriptLogger.java @@ -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); diff --git a/source/java/org/alfresco/repo/jscript/ScriptUtils.java b/source/java/org/alfresco/repo/jscript/ScriptUtils.java index b486f34bc6..2b5476f7e9 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptUtils.java +++ b/source/java/org/alfresco/repo/jscript/ScriptUtils.java @@ -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; diff --git a/source/java/org/alfresco/repo/jscript/Search.java b/source/java/org/alfresco/repo/jscript/Search.java index 6c030dc9ce..0f2933600f 100644 --- a/source/java/org/alfresco/repo/jscript/Search.java +++ b/source/java/org/alfresco/repo/jscript/Search.java @@ -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; diff --git a/source/java/org/alfresco/repo/jscript/Session.java b/source/java/org/alfresco/repo/jscript/Session.java index fd73d712a3..aa3cf0b09e 100644 --- a/source/java/org/alfresco/repo/jscript/Session.java +++ b/source/java/org/alfresco/repo/jscript/Session.java @@ -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; diff --git a/source/java/org/alfresco/repo/module/tool/default-file-mapping.properties b/source/java/org/alfresco/repo/module/tool/default-file-mapping.properties index 785ecceb06..ab22f4cace 100644 --- a/source/java/org/alfresco/repo/module/tool/default-file-mapping.properties +++ b/source/java/org/alfresco/repo/module/tool/default-file-mapping.properties @@ -5,4 +5,5 @@ /web/jsp=/jsp /web/css=/css /web/images=/images -/web/scripts=/scripts \ No newline at end of file +/web/scripts=/scripts +/web/php=/php \ No newline at end of file diff --git a/source/java/org/alfresco/repo/processor/BaseProcessor.java b/source/java/org/alfresco/repo/processor/BaseProcessor.java new file mode 100644 index 0000000000..53c693aea2 --- /dev/null +++ b/source/java/org/alfresco/repo/processor/BaseProcessor.java @@ -0,0 +1,158 @@ +/* + * 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.processor; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.Processor; +import org.alfresco.service.cmr.repository.ProcessorExtension; +import org.alfresco.service.cmr.repository.ScriptProcessor; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.TemplateProcessor; +import org.alfresco.service.cmr.repository.TemplateService; + +/** + * Base class of a processor, encapsulates the implementation reguarding the registration of the processor + * with the relevant services and the handling of processor extensions. + * + * @author Roy Wetherall + */ +public abstract class BaseProcessor implements Processor +{ + /** The name of the processor */ + protected String name; + + /** The file extension that this processor understands */ + protected String extension; + + /** The script service */ + protected ScriptService scriptService; + + /** The template service */ + protected TemplateService templateService; + + /** The service registry */ + protected ServiceRegistry services; + + /** A map containing all the processor extenstions */ + protected Map processExtensions = new HashMap(10); + + /** + * Registers this processor with the relevant services + */ + public void register() + { + if (this instanceof ScriptProcessor) + { + scriptService.registerScriptProcessor((ScriptProcessor)this); + } + if (this instanceof TemplateProcessor) + { + templateService.registerTemplateProcessor((TemplateProcessor)this); + } + } + + /** + * Sets the script service + * + * @param scriptService the script service + */ + public void setScriptService(ScriptService scriptService) + { + this.scriptService = scriptService; + } + + /** + * Sets the template service + * + * @param templateService the template service + */ + public void setTemplateService(TemplateService templateService) + { + this.templateService = templateService; + } + + /** + * Sets the service registry + * + * @param serviceRegistry the service registry + */ + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.services = serviceRegistry; + } + + /** + * Get the name of the processor + * + * @return String the name of the processor + */ + public String getName() + { + return name; + } + + /** + * Sets the name of the processor + * + * @param name the name of the processor + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Gets the extension that the processor understands + * + * @return String the extension + */ + public String getExtension() + { + return extension; + } + + /** + * Sets the extenstion that the processor understands + * + * @param extension the extension + */ + public void setExtension(String extension) + { + this.extension = extension; + } + + /** + * Registers a processor extension with the processor + * + * @param processorExtension the processor extension + */ + public void registerProcessorExtension(ProcessorExtension processorExtension) + { + this.processExtensions.put(processorExtension.getExtensionName(), processorExtension); + } +} diff --git a/source/java/org/alfresco/repo/jscript/BaseScriptImplementation.java b/source/java/org/alfresco/repo/processor/BaseProcessorExtension.java similarity index 60% rename from source/java/org/alfresco/repo/jscript/BaseScriptImplementation.java rename to source/java/org/alfresco/repo/processor/BaseProcessorExtension.java index 584aaca525..31f78254d8 100644 --- a/source/java/org/alfresco/repo/jscript/BaseScriptImplementation.java +++ b/source/java/org/alfresco/repo/processor/BaseProcessorExtension.java @@ -22,32 +22,32 @@ * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ -package org.alfresco.repo.jscript; +package org.alfresco.repo.processor; -import org.alfresco.service.cmr.repository.ScriptImplementation; -import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.Processor; +import org.alfresco.service.cmr.repository.ProcessorExtension; /** - * Abstract base class for a script implementation + * Abstract base class for a processor extension * * @author Roy Wetherall */ -public abstract class BaseScriptImplementation implements ScriptImplementation +public abstract class BaseProcessorExtension implements ProcessorExtension { - /** The script service */ - private ScriptService scriptService; + /** The processor */ + private Processor processor; - /** The name of the script */ - private String scriptName; + /** The name of the extension */ + private String extensionName; /** - * Sets the script service + * Sets the processor * - * @param scriptService the script service + * @param scriptProcessor the processor */ - public void setScriptService(ScriptService scriptService) + public void setProcessor(Processor processor) { - this.scriptService = scriptService; + this.processor = processor; } /** @@ -55,24 +55,24 @@ public abstract class BaseScriptImplementation implements ScriptImplementation */ public void register() { - this.scriptService.registerScript(this); + this.processor.registerProcessorExtension(this); } /** - * Sets the script name + * Sets the extension name * - * @param scriptName the script name + * @param extensionName the extension name */ - public void setScriptName(String scriptName) + public void setExtensionName(String extension) { - this.scriptName = scriptName; + this.extensionName = extension; } /** - * @see org.alfresco.service.cmr.repository.ScriptImplementation#getScriptName() + * @see org.alfresco.service.cmr.repository.ProcessorExtension#getExtensionName() */ - public String getScriptName() + public String getExtensionName() { - return this.scriptName; + return this.extensionName; } } diff --git a/source/java/org/alfresco/repo/processor/ScriptServiceImpl.java b/source/java/org/alfresco/repo/processor/ScriptServiceImpl.java new file mode 100644 index 0000000000..a38fbb8ec1 --- /dev/null +++ b/source/java/org/alfresco/repo/processor/ScriptServiceImpl.java @@ -0,0 +1,403 @@ +/* + * 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.processor; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +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.ScriptLocation; +import org.alfresco.service.cmr.repository.ScriptProcessor; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.log4j.Logger; + +/** + * Script service implementation + * + * @author Kevin Roast + * @author Roy Wetherall + */ +public class ScriptServiceImpl implements ScriptService +{ + /** Logger */ + private static final Logger logger = Logger.getLogger(ScriptServiceImpl.class); + + /** The name of the default script processor */ + private String defaultScriptProcessor; + + /** Maps containing the script processors */ + private Map scriptProcessors = new HashMap(5); + private Map scriptProcessorNamesByExtension = new HashMap(5); + + /** The node service */ + private NodeService nodeService; + + /** + * Sets the name of the default script processor + * + * @param defaultScriptProcessor the name of the default script processor + */ + public void setDefaultScriptProcessor(String defaultScriptProcessor) + { + this.defaultScriptProcessor = defaultScriptProcessor; + } + + /** + * Set the node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Register a script processor + * + * @param scriptProcessor the script processor to register with the script service + */ + public void registerScriptProcessor(ScriptProcessor scriptProcessor) + { + this.scriptProcessors.put(scriptProcessor.getName(), scriptProcessor); + this.scriptProcessorNamesByExtension.put(scriptProcessor.getExtension(), scriptProcessor.getName()); + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScript(java.lang.String, java.util.Map) + */ + public Object executeScript(String scriptClasspath, Map model) + throws ScriptException + { + if (scriptClasspath == null) + { + throw new IllegalArgumentException("Script ClassPath is mandatory."); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Executing script: " + scriptClasspath); + } + + try + { + ScriptProcessor scriptProcessor = getScriptProcessor(scriptClasspath); + return scriptProcessor.execute(scriptClasspath, model); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute script '" + scriptClasspath + "': " + err.getMessage(), err); + } + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScript(java.lang.String, java.lang.String, java.util.Map) + */ + public Object executeScript(String engine, String scriptClasspath, Map model) + throws ScriptException + { + if (scriptClasspath == null) + { + throw new IllegalArgumentException("Script ClassPath is mandatory."); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Executing script: " + scriptClasspath); + } + + try + { + ScriptProcessor scriptProcessor = lookupScriptProcessor(engine); + return scriptProcessor.execute(scriptClasspath, 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 model) + throws ScriptException + { + if (scriptRef == null) + { + throw new IllegalArgumentException("Script NodeRef is mandatory."); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Executing script: " + scriptRef.toString()); + } + + try + { + ScriptProcessor scriptProcessor = getScriptProcessor(scriptRef); + return scriptProcessor.execute(scriptRef, contentProp, model); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute script '" + scriptRef.toString() + "': " + err.getMessage(), err); + } + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScript(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.util.Map) + */ + public Object executeScript(String engine, NodeRef scriptRef, QName contentProp, Map model) + throws ScriptException + { + if (scriptRef == null) + { + throw new IllegalArgumentException("Script NodeRef is mandatory."); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Executing script: " + scriptRef.toString()); + } + + try + { + ScriptProcessor scriptProcessor = lookupScriptProcessor(engine); + return scriptProcessor.execute(scriptRef, contentProp, 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 model) + throws ScriptException + { + ParameterCheck.mandatory("Location", location); + + if (logger.isDebugEnabled()) + { + logger.debug("Executing script: " + location.toString()); + } + + try + { + ScriptProcessor scriptProcessor = getScriptProcessor(location.toString()); + return scriptProcessor.execute(location, model); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute script '" + location.toString() + "': " + err.getMessage(), err); + } + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScript(java.lang.String, org.alfresco.service.cmr.repository.ScriptLocation, java.util.Map) + */ + public Object executeScript(String engine, ScriptLocation location, Map model) + throws ScriptException + { + ParameterCheck.mandatory("Location", location); + + if (logger.isDebugEnabled()) + { + logger.debug("Executing script: " + location.toString()); + } + + try + { + ScriptProcessor scriptProcessor = lookupScriptProcessor(engine); + return scriptProcessor.execute(location, model); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute script '" + location.toString() + "': " + err.getMessage(), err); + } + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map) + */ + public Object executeScriptString(String script, Map model) + throws ScriptException + { + return executeScriptString(this.defaultScriptProcessor, script, model); + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map) + */ + public Object executeScriptString(String engine, String script, Map 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); + } + + try + { + ScriptProcessor scriptProcessor = lookupScriptProcessor(engine); + return scriptProcessor.executeString(script, model); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err); + } + } + + /** + * Helper method to lookup the script proecessor based on a name + * + * @param name the name of the script processor + * @return ScriptProcessor the script processor, default processor if no match found + */ + protected ScriptProcessor lookupScriptProcessor(String name) + { + ScriptProcessor scriptProcessor = this.scriptProcessors.get(name); + if (scriptProcessor == null) + { + scriptProcessor = this.scriptProcessors.get(this.defaultScriptProcessor); + } + return scriptProcessor; + } + + /** + * Gets a scipt processor based on the node reference of a script + * + * @param scriptNode the node reference of the script + * @return ScriptProcessor the script processor + */ + protected ScriptProcessor getScriptProcessor(NodeRef scriptNode) + { + String scriptName = (String)this.nodeService.getProperty(scriptNode, ContentModel.PROP_NAME); + return getScriptProcessorImpl(scriptName); + } + + /** + * Gets a script processor based on the script location string + * + * @param scriptLocation the script location + * @return ScriptProcessor the script processor + */ + protected ScriptProcessor getScriptProcessor(String scriptLocation) + { + if (scriptLocation.indexOf(StoreRef.URI_FILLER) != -1) + { + // Try and create the nodeRef + NodeRef nodeRef = new NodeRef(scriptLocation); + scriptLocation = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + } + + return getScriptProcessorImpl(scriptLocation); + } + + /** + * Gets a script processor based on the scripts file name + * + * @param scriptFileName the scripts file name + * @return ScriptProcessor the matching script processor + */ + protected ScriptProcessor getScriptProcessorImpl(String scriptFileName) + { + String engine = null; + + if (scriptFileName != null) + { + String extension = getFileExtension(scriptFileName); + if (extension != null) + { + engine = this.scriptProcessorNamesByExtension.get(extension); + } + } + + return lookupScriptProcessor(engine); + } + + /** + * Gets the file extension of a file + * + * @param fileName the file name + * @return the file extension + */ + private String getFileExtension(String fileName) + { + String extension = null; + int index = fileName.lastIndexOf('.'); + if (index > -1 && (index < fileName.length() - 1)) + { + extension = fileName.substring(index + 1); + } + return extension; + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptService#buildDefaultModel(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + public Map buildDefaultModel( + NodeRef person, + NodeRef companyHome, + NodeRef userHome, + NodeRef script, + NodeRef document, + NodeRef space) + { + Map model = new HashMap(); + + // add the well known node wrapper objects + model.put("companyhome", companyHome); + model.put("userhome", userHome); + model.put("person", person); + if (script != null) + { + model.put("script", script); + } + if (document != null) + { + model.put("document", document); + } + if (space != null) + { + model.put("space", space); + } + + return model; + } +} diff --git a/source/java/org/alfresco/repo/processor/TemplateServiceImpl.java b/source/java/org/alfresco/repo/processor/TemplateServiceImpl.java new file mode 100644 index 0000000000..7b59724ba7 --- /dev/null +++ b/source/java/org/alfresco/repo/processor/TemplateServiceImpl.java @@ -0,0 +1,262 @@ +/* + * 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.processor; + +import java.io.StringWriter; +import java.io.Writer; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.TemplateException; +import org.alfresco.service.cmr.repository.TemplateImageResolver; +import org.alfresco.service.cmr.repository.TemplateProcessor; +import org.alfresco.service.cmr.repository.TemplateService; + +/** + * Implementation of the TemplateService using Spring configured script engines. + * + * @author Kevin Roast + */ +public class TemplateServiceImpl implements TemplateService +{ + /** Default Template processor engine to use */ + private String defaultTemplateEngine; + + /** List of available template processors */ + private Map processors = new HashMap(5); + private Map processorNamesByExtension = new HashMap(5); + + /** The node service */ + private NodeService nodeService; + + /** + * @param defaultTemplateEngine The default Template Engine name to set. + */ + public void setDefaultTemplateEngine(String defaultTemplateEngine) + { + this.defaultTemplateEngine = defaultTemplateEngine; + } + + /** + * Set the node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#getTemplateProcessor(java.lang.String) + */ + public TemplateProcessor getTemplateProcessor(String engine) + { + return this.processors.get(engine); + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#registerTemplateProcessor(org.alfresco.service.cmr.repository.TemplateProcessor) + */ + public void registerTemplateProcessor(TemplateProcessor templateProcessor) + { + this.processors.put(templateProcessor.getName(), templateProcessor); + this.processorNamesByExtension.put(templateProcessor.getExtension(), templateProcessor.getName()); + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#processTemplate(java.lang.String, java.lang.Object) + */ + public String processTemplate(String template, Object model) throws TemplateException + { + return processTemplate(getTemplateProcessorName(template), template, model); + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#processTemplate(java.lang.String, java.lang.Object, java.io.Writer) + */ + public void processTemplate(String template, Object model, Writer out) throws TemplateException + { + processTemplate(getTemplateProcessorName(template), template, model, out); + } + + /** + * Gets the template processor name from the template string + * + * @param template the template string location + * @return the template processor string + */ + private String getTemplateProcessorName(String template) + { + String engine = null; + + try + { + // Try and create the nodeRef + NodeRef templateNodeRef = new NodeRef(template); + String templateName = (String)this.nodeService.getProperty(templateNodeRef, ContentModel.PROP_NAME); + String extension = getFileExtension(templateName); + if (extension != null) + { + engine = this.processorNamesByExtension.get(extension); + } + } + catch (AlfrescoRuntimeException exception) + { + // Presume that the provided template is a classpath + String extension = getFileExtension(template); + if (extension != null) + { + engine = this.processorNamesByExtension.get(extension); + } + } + + // Set the default engine if none found + if (engine == null) + { + engine = this.defaultTemplateEngine; + } + + return engine; + } + + /** + * Gets the file extension of a file + * + * @param fileName the file name + * @return the file extension + */ + private String getFileExtension(String fileName) + { + String extension = null; + int index = fileName.lastIndexOf('.'); + if (index > -1 && (index < fileName.length() - 1)) + { + extension = fileName.substring(index + 1); + } + return extension; + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#processTemplate(java.lang.String, java.lang.String, java.lang.Object, java.io.Writer) + */ + public void processTemplate(String engine, String template, Object model, Writer out) + throws TemplateException + { + try + { + // execute template processor + TemplateProcessor processor = getTemplateProcessor(engine); + processor.process(template, model, out); + } + catch (TemplateException terr) + { + throw terr; + } + catch (Throwable err) + { + throw new TemplateException(err.getMessage(), err); + } + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#processTemplate(java.lang.String, java.lang.String, java.lang.Object) + */ + public String processTemplate(String engine, String template, Object model) + throws TemplateException + { + Writer out = new StringWriter(1024); + processTemplate(engine, template, model, out); + return out.toString(); + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#processTemplateString(java.lang.String, java.lang.String, java.lang.Object, java.io.Writer) + */ + public void processTemplateString(String engine, String template, Object model, Writer out) + throws TemplateException + { + try + { + // execute template processor + TemplateProcessor processor = getTemplateProcessor(engine); + processor.processString(template, model, out); + } + catch (TemplateException terr) + { + throw terr; + } + catch (Throwable err) + { + throw new TemplateException(err.getMessage(), err); + } + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#processTemplateString(java.lang.String, java.lang.String, java.lang.Object) + */ + public String processTemplateString(String engine, String template, Object model) + throws TemplateException + { + Writer out = new StringWriter(1024); + processTemplateString(engine, template, model, out); + return out.toString(); + } + + /** + * @see org.alfresco.service.cmr.repository.TemplateService#buildDefaultModel(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.TemplateImageResolver) + */ + public Map buildDefaultModel(NodeRef person, NodeRef companyHome, NodeRef userHome, NodeRef template, TemplateImageResolver imageResolver) + { + Map model = new HashMap(16, 1.0f); + + // Place the image resolver into the model + if (imageResolver != null) + { + model.put(KEY_IMAGE_RESOLVER, imageResolver); + } + + // Put the common node reference into the model + model.put(KEY_COMPANY_HOME, companyHome); + model.put(KEY_USER_HOME, userHome); + model.put(KEY_PERSON, person); + + // add the template itself as "template" if it comes from content on a node + if (template != null) + { + model.put(KEY_TEMPLATE, template); + } + + // current date/time is useful to have and isn't supplied by FreeMarker by default + model.put(KEY_DATE, new Date()); + + return model; + } +} diff --git a/source/java/org/alfresco/repo/template/AVM.java b/source/java/org/alfresco/repo/template/AVM.java index d6b9d98fca..d86ef915af 100644 --- a/source/java/org/alfresco/repo/template/AVM.java +++ b/source/java/org/alfresco/repo/template/AVM.java @@ -34,7 +34,7 @@ import org.alfresco.service.cmr.avm.AVMStoreDescriptor; * * @author Kevin Roast */ -public class AVM extends BaseTemplateExtensionImplementation +public class AVM extends BaseTemplateProcessorExtension { private ServiceRegistry services; diff --git a/source/java/org/alfresco/repo/template/BaseTemplateExtensionImplementation.java b/source/java/org/alfresco/repo/template/BaseTemplateProcessorExtension.java similarity index 55% rename from source/java/org/alfresco/repo/template/BaseTemplateExtensionImplementation.java rename to source/java/org/alfresco/repo/template/BaseTemplateProcessorExtension.java index 82d4a32bf7..80d38e5676 100644 --- a/source/java/org/alfresco/repo/template/BaseTemplateExtensionImplementation.java +++ b/source/java/org/alfresco/repo/template/BaseTemplateProcessorExtension.java @@ -24,37 +24,22 @@ */ package org.alfresco.repo.template; -import org.alfresco.service.cmr.repository.TemplateExtensionImplementation; +import org.alfresco.repo.processor.BaseProcessorExtension; +import org.alfresco.service.cmr.repository.TemplateProcessorExtension; import org.alfresco.service.cmr.repository.TemplateImageResolver; -import org.alfresco.service.cmr.repository.TemplateService; /** * Abstract base class for a template extension implementation * * @author Kevin Roast */ -public abstract class BaseTemplateExtensionImplementation implements TemplateExtensionImplementation -{ - /** The template service instance */ - private TemplateService templateService; - - /** The name of the template extension */ - private String extensionName; - +public abstract class BaseTemplateProcessorExtension extends BaseProcessorExtension implements TemplateProcessorExtension +{ /** The TemplateImageResolver for the current template execution thread */ private ThreadLocal resolver = new ThreadLocal(); - /** - * @param templateService The TemplateService to set. - */ - public void setTemplateService(TemplateService templateService) - { - this.templateService = templateService; - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateExtensionImplementation#setTemplateImageResolver(org.alfresco.service.cmr.repository.TemplateImageResolver) + * @see org.alfresco.service.cmr.repository.TemplateProcessorExtension#setTemplateImageResolver(org.alfresco.service.cmr.repository.TemplateImageResolver) */ public void setTemplateImageResolver(TemplateImageResolver resolver) { @@ -62,36 +47,10 @@ public abstract class BaseTemplateExtensionImplementation implements TemplateExt } /** - * @see org.alfresco.service.cmr.repository.TemplateExtensionImplementation#getTemplateImageResolver() + * @see org.alfresco.service.cmr.repository.TemplateProcessorExtension#getTemplateImageResolver() */ public TemplateImageResolver getTemplateImageResolver() { return this.resolver.get(); } - - /** - * Registers this template extension with the Template Service - */ - public void register() - { - this.templateService.registerExtension(this); - } - - /** - * Returns the name of the template extension - * - * @return the name of the template extension - */ - public String getExtensionName() - { - return extensionName; - } - - /** - * @param extensionName The template extension name. - */ - public void setExtensionName(String extensionName) - { - this.extensionName = extensionName; - } } diff --git a/source/java/org/alfresco/repo/template/Classification.java b/source/java/org/alfresco/repo/template/Classification.java index 555527cdfc..a974eaf551 100644 --- a/source/java/org/alfresco/repo/template/Classification.java +++ b/source/java/org/alfresco/repo/template/Classification.java @@ -40,7 +40,7 @@ import org.alfresco.service.namespace.QName; * * @author Andy Hind */ -public class Classification extends BaseTemplateExtensionImplementation +public class Classification extends BaseTemplateProcessorExtension { private ServiceRegistry services; private StoreRef storeRef; diff --git a/source/java/org/alfresco/repo/template/DateCompareMethod.java b/source/java/org/alfresco/repo/template/DateCompareMethod.java index 0f97f55025..7ae84c1da8 100644 --- a/source/java/org/alfresco/repo/template/DateCompareMethod.java +++ b/source/java/org/alfresco/repo/template/DateCompareMethod.java @@ -43,7 +43,7 @@ import freemarker.template.TemplateNumberModel; * dateCompare(dateA, dateB) - 1 if dateA if greater than dateB * dateCompare(dateA, dateB, millis) - 1 if dateA is greater than dateB by at least millis, else 0 */ -public class DateCompareMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class DateCompareMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/repo/template/DateIncrementMethod.java b/source/java/org/alfresco/repo/template/DateIncrementMethod.java index 75f3c5ebd0..9708b301d0 100644 --- a/source/java/org/alfresco/repo/template/DateIncrementMethod.java +++ b/source/java/org/alfresco/repo/template/DateIncrementMethod.java @@ -35,7 +35,7 @@ import freemarker.template.TemplateNumberModel; /** * @author Roy Wetherall */ -public class DateIncrementMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class DateIncrementMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java index aa55c358e8..e2bb36e4a6 100644 --- a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java +++ b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java @@ -26,18 +26,17 @@ package org.alfresco.repo.template; import java.io.IOException; import java.io.Writer; -import java.util.Date; import java.util.HashMap; import java.util.Map; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.repo.processor.BaseProcessor; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ProcessorExtension; import org.alfresco.service.cmr.repository.TemplateException; -import org.alfresco.service.cmr.repository.TemplateExtensionImplementation; import org.alfresco.service.cmr.repository.TemplateImageResolver; import org.alfresco.service.cmr.repository.TemplateProcessor; +import org.alfresco.service.cmr.repository.TemplateProcessorExtension; +import org.alfresco.service.cmr.repository.TemplateService; import org.apache.log4j.Logger; import freemarker.cache.MruCacheStorage; @@ -61,7 +60,7 @@ import freemarker.template.TemplateExceptionHandler; * * @author Kevin Roast */ -public class FreeMarkerProcessor implements TemplateProcessor +public class FreeMarkerProcessor extends BaseProcessor implements TemplateProcessor { private final static String MSG_ERROR_NO_TEMPLATE = "error_no_template"; private final static String MSG_ERROR_TEMPLATE_FAIL = "error_template_fail"; @@ -72,35 +71,9 @@ public class FreeMarkerProcessor implements TemplateProcessor /** Pseudo path to String based template */ private static final String PATH = "string://fixed"; - /** The permission-safe node service */ - private NodeService nodeService; - - /** The Content Service to use */ - private ContentService contentService; - /** Template encoding */ private String defaultEncoding; - /** - * Set the node service - * - * @param nodeService The permission-safe node service - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - /** - * Set the content service - * - * @param contentService The ContentService to use - */ - public void setContentService(ContentService contentService) - { - this.contentService = contentService; - } - /** * Set the default template encoding * @@ -124,7 +97,7 @@ public class FreeMarkerProcessor implements TemplateProcessor config.setCacheStorage(new MruCacheStorage(2, 0)); // use our custom loader to find templates on the ClassPath - config.setTemplateLoader(new ClassPathRepoTemplateLoader(nodeService, contentService)); + config.setTemplateLoader(new ClassPathRepoTemplateLoader(this.services.getNodeService(), this.services.getContentService())); // use our custom object wrapper that can deal with QNameMap objects directly config.setObjectWrapper(new QNameAwareObjectWrapper()); @@ -213,7 +186,8 @@ public class FreeMarkerProcessor implements TemplateProcessor try { // perform the template processing against supplied data model - t.process(model, out); + Object freeMarkerModel = convertToFreeMarkerModel(model); + t.process(freeMarkerModel, out); } catch (Throwable err) { @@ -270,7 +244,8 @@ public class FreeMarkerProcessor implements TemplateProcessor try { // perform the template processing against supplied data model - t.process(model, out); + Object freeMarkerModel = convertToFreeMarkerModel(model); + t.process(freeMarkerModel, out); if (logger.isDebugEnabled()) { @@ -294,58 +269,52 @@ public class FreeMarkerProcessor implements TemplateProcessor } } - /** - * Create the default data-model available to templates as global objects. - *

- * 'companyhome' - the Company Home node
- * 'userhome' - the current user home space node
- * 'person' - the node representing the current user Person
- * 'template' - the node representing the template itself (may not be available) - *

- * Also adds various helper util objects and methods. - * - * @param services ServiceRegistry - * @param person The current user Person Node - * @param companyHome The CompanyHome ref - * @param userHome The User home space ref - * @param template Optional ref to the template itself - * @param resolver Image resolver to resolve icon images etc. - * - * @return A Map of Templatable Node objects and util objects. - */ - public static Map buildDefaultModel( - ServiceRegistry services, - NodeRef person, NodeRef companyHome, NodeRef userHome, NodeRef template, - TemplateImageResolver imageResolver) + private Object convertToFreeMarkerModel(Object model) { - Map model = new HashMap(16, 1.0f); - - // supply the Company Home space as "companyhome" - model.put("companyhome", new TemplateNode(companyHome, services, imageResolver)); - - // supply the users Home Space as "userhome" - model.put("userhome", new TemplateNode(userHome, services, imageResolver)); - - // supply the current user Node as "person" - model.put("person", new TemplateNode(person, services, imageResolver)); - - // add the template itself as "template" if it comes from content on a node - if (template != null) - { - model.put("template", new TemplateNode(template, services, imageResolver)); + // If we dont have a map in our hand we just return the passes model + if (model instanceof Map) + { + Map freeMarkerModel = new HashMap(((Map)model).size()); + + // Look for the image resolver in the model + TemplateImageResolver imageResolver = (TemplateImageResolver)((Map)model).get(TemplateService.KEY_IMAGE_RESOLVER); + + for (Object objKey : ((Map)model).keySet()) + { + String key = (String)objKey; + if (key.equals(TemplateService.KEY_IMAGE_RESOLVER) == false) + { + Object value = ((Map)model).get(key); + if (value instanceof NodeRef) + { + // Concer the node reference to a template node + freeMarkerModel.put(key, new TemplateNode((NodeRef)value, this.services, imageResolver)); + } + else + { + // Just add the objec to the free marker model + freeMarkerModel.put(key, ((Map)model).get(key)); + } + } + } + + + // add the template extensions to the model + // the extensions include custom root helper objects and custom template method objects + for (ProcessorExtension ext : this.processExtensions.values()) + { + if (ext instanceof TemplateProcessorExtension) + { + ((TemplateProcessorExtension)ext).setTemplateImageResolver(imageResolver); + } + freeMarkerModel.put(ext.getExtensionName(), ext); + } + + return freeMarkerModel; } - - // current date/time is useful to have and isn't supplied by FreeMarker by default - model.put("date", new Date()); - - // add the template extensions to the model - // the extensions include custom root helper objects and custom template method objects - for (TemplateExtensionImplementation ext : services.getTemplateService().getExtensions()) + else { - ext.setTemplateImageResolver(imageResolver); - model.put(ext.getExtensionName(), ext); + return model; } - - return model; } } diff --git a/source/java/org/alfresco/repo/template/HasAspectMethod.java b/source/java/org/alfresco/repo/template/HasAspectMethod.java index f08e4d6701..c9afb2a2b9 100644 --- a/source/java/org/alfresco/repo/template/HasAspectMethod.java +++ b/source/java/org/alfresco/repo/template/HasAspectMethod.java @@ -42,7 +42,7 @@ import freemarker.template.TemplateScalarModel; *

* Usage: hasAspect(TemplateNode node, String aspect) - 1 on true, 0 on false */ -public class HasAspectMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class HasAspectMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/repo/template/HasPermissionMethod.java b/source/java/org/alfresco/repo/template/HasPermissionMethod.java index 2f1bc8e28c..ff3cf694cb 100644 --- a/source/java/org/alfresco/repo/template/HasPermissionMethod.java +++ b/source/java/org/alfresco/repo/template/HasPermissionMethod.java @@ -41,7 +41,7 @@ import freemarker.template.TemplateScalarModel; *

* Usage: hasPermission(TemplateNode node, String permission) - 1 on true, 0 on false */ -public class HasPermissionMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class HasPermissionMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/repo/template/I18NMessageMethod.java b/source/java/org/alfresco/repo/template/I18NMessageMethod.java index 693560a6d5..42fadefd49 100644 --- a/source/java/org/alfresco/repo/template/I18NMessageMethod.java +++ b/source/java/org/alfresco/repo/template/I18NMessageMethod.java @@ -41,7 +41,7 @@ import freemarker.template.TemplateScalarModel; *

* Usage: message(String id) */ -public class I18NMessageMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class I18NMessageMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/repo/template/ISO8601DateFormatMethod.java b/source/java/org/alfresco/repo/template/ISO8601DateFormatMethod.java index cc1b733f3b..54e8fb64b5 100644 --- a/source/java/org/alfresco/repo/template/ISO8601DateFormatMethod.java +++ b/source/java/org/alfresco/repo/template/ISO8601DateFormatMethod.java @@ -41,7 +41,7 @@ import freemarker.template.TemplateModelException; *

* Usage: xmldate(Date date) */ -public class ISO8601DateFormatMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class ISO8601DateFormatMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/repo/template/Session.java b/source/java/org/alfresco/repo/template/Session.java index 9855f8802a..b44c4b6080 100644 --- a/source/java/org/alfresco/repo/template/Session.java +++ b/source/java/org/alfresco/repo/template/Session.java @@ -32,7 +32,7 @@ import org.alfresco.service.cmr.repository.TemplateImageResolver; * * @author Andy Hind */ -public class Session extends BaseTemplateExtensionImplementation +public class Session extends BaseTemplateProcessorExtension { private ServiceRegistry services; diff --git a/source/java/org/alfresco/repo/template/TemplateServiceImpl.java b/source/java/org/alfresco/repo/template/TemplateServiceImpl.java deleted file mode 100644 index 66cff32385..0000000000 --- a/source/java/org/alfresco/repo/template/TemplateServiceImpl.java +++ /dev/null @@ -1,271 +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.template; - -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.service.cmr.repository.ScriptImplementation; -import org.alfresco.service.cmr.repository.TemplateException; -import org.alfresco.service.cmr.repository.TemplateExtensionImplementation; -import org.alfresco.service.cmr.repository.TemplateProcessor; -import org.alfresco.service.cmr.repository.TemplateService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -/** - * Implementation of the TemplateService using Spring configured template engines. - * - * @author Kevin Roast - */ -public class TemplateServiceImpl implements TemplateService, ApplicationContextAware -{ - private static Log logger = LogFactory.getLog(TemplateService.class); - - /** Spring ApplicationContext for bean lookup by ID */ - private ApplicationContext applicationContext; - - /** Default Template processor engine to use */ - private String defaultTemplateEngine; - - /** Available template engine names to impl class names */ - private Map templateEngines; - - /** Threadlocal instance for template processor cache */ - private static ThreadLocal> processors = new ThreadLocal>(); - - /** List of global template extensions */ - private List globalExtensions = new ArrayList(); - - - /** - * Set the application context - * - * @param applicationContext the application context - */ - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - this.applicationContext = applicationContext; - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#registerExtension(org.alfresco.service.cmr.repository.TemplateExtensionImplementation) - */ - public void registerExtension(TemplateExtensionImplementation extension) - { - this.globalExtensions.add(extension); - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#getExtensions() - */ - public List getExtensions() - { - return this.globalExtensions; - } - - /** - * @param defaultTemplateEngine The default Template Engine name to set. - */ - public void setDefaultTemplateEngine(String defaultTemplateEngine) - { - this.defaultTemplateEngine = defaultTemplateEngine; - } - - /** - * @param templateEngines The Map of template engine name to impl class name to set. - */ - public void setTemplateEngines(Map templateEngines) - { - this.templateEngines = templateEngines; - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#getTemplateProcessor(java.lang.String) - */ - public TemplateProcessor getTemplateProcessor(String engine) - { - try - { - return getTemplateProcessorImpl(engine); - } - catch (Throwable err) - { - if (logger.isDebugEnabled()) - logger.debug("Unable to load template processor.", err); - - return null; - } - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#processTemplate(java.lang.String, java.lang.String, java.lang.Object, java.io.Writer) - */ - public void processTemplate(String engine, String template, Object model, Writer out) - throws TemplateException - { - try - { - // execute template processor - TemplateProcessor processor = getTemplateProcessorImpl(engine); - processor.process(template, model, out); - } - catch (TemplateException terr) - { - throw terr; - } - catch (Throwable err) - { - throw new TemplateException(err.getMessage(), err); - } - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#processTemplate(java.lang.String, java.lang.String, java.lang.Object) - */ - public String processTemplate(String engine, String template, Object model) - throws TemplateException - { - Writer out = new StringWriter(1024); - processTemplate(engine, template, model, out); - return out.toString(); - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#processTemplateString(java.lang.String, java.lang.String, java.lang.Object, java.io.Writer) - */ - public void processTemplateString(String engine, String template, Object model, Writer out) - throws TemplateException - { - try - { - // execute template processor - TemplateProcessor processor = getTemplateProcessorImpl(engine); - processor.processString(template, model, out); - } - catch (TemplateException terr) - { - throw terr; - } - catch (Throwable err) - { - throw new TemplateException(err.getMessage(), err); - } - } - - /** - * @see org.alfresco.service.cmr.repository.TemplateService#processTemplateString(java.lang.String, java.lang.String, java.lang.Object) - */ - public String processTemplateString(String engine, String template, Object model) - throws TemplateException - { - Writer out = new StringWriter(1024); - processTemplateString(engine, template, model, out); - return out.toString(); - } - - /** - * Return the TemplateProcessor implementation for the named template engine - * - * @param name Template Engine name - * - * @return TemplateProcessor - */ - private TemplateProcessor getTemplateProcessorImpl(String name) - { - // use the ThreadLocal map to find the processors instance - // create the cache map for this thread if required - Map procMap = processors.get(); - if (procMap == null) - { - procMap = new HashMap(2, 1.0f); - processors.set(procMap); - } - - if (name == null) - { - name = defaultTemplateEngine; - } - - // find the impl for the named processor - TemplateProcessor processor = procMap.get(name); - if (processor == null) - { - String className = templateEngines.get(name); - if (className == null) - { - throw new AlfrescoRuntimeException("Unable to find configured ClassName for template engine: " + name); - } - try - { - Object obj; - try - { - obj = this.applicationContext.getBean(className); - } - catch (BeansException err) - { - // instantiate the processor class directory if not a Spring bean - obj = Class.forName(className).newInstance(); - } - - if (obj instanceof TemplateProcessor) - { - processor = (TemplateProcessor)obj; - } - else - { - throw new AlfrescoRuntimeException("Supplied template processors does not implement TemplateProcessor: " + className); - } - } - catch (ClassNotFoundException err1) - { - // if the bean is not a classname, then it may be a spring bean Id - throw new AlfrescoRuntimeException("Unable to load class for supplied template processors: " + className, err1); - } - catch (IllegalAccessException err2) - { - throw new AlfrescoRuntimeException("Unable to load class for supplied template processors: " + className, err2); - } - catch (InstantiationException err3) - { - throw new AlfrescoRuntimeException("Unable to instantiate class for supplied template processors: " + className, err3); - } - - // cache for later - procMap.put(name, processor); - } - - return processor; - } -} diff --git a/source/java/org/alfresco/repo/template/UrlEncodeMethod.java b/source/java/org/alfresco/repo/template/UrlEncodeMethod.java index 42820dabff..140652a0e9 100644 --- a/source/java/org/alfresco/repo/template/UrlEncodeMethod.java +++ b/source/java/org/alfresco/repo/template/UrlEncodeMethod.java @@ -41,7 +41,7 @@ import freemarker.template.TemplateScalarModel; *

* Usage: urlencode(String url) */ -public class UrlEncodeMethod extends BaseTemplateExtensionImplementation implements TemplateMethodModelEx +public class UrlEncodeMethod extends BaseTemplateProcessorExtension implements TemplateMethodModelEx { /** * @see freemarker.template.TemplateMethodModel#exec(java.util.List) diff --git a/source/java/org/alfresco/service/cmr/repository/Processor.java b/source/java/org/alfresco/service/cmr/repository/Processor.java new file mode 100644 index 0000000000..029f3c35c3 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/Processor.java @@ -0,0 +1,55 @@ +/* + * 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.service.cmr.repository; + + +/** + * Processor interface. + * + * @author Roy Wetherall + */ +public interface Processor +{ + /** + * Get the name of the processor + * + * @return the name of the processor + */ + public String getName(); + + /** + * The file extension that the processor is associated with, null if none. + * + * @return the extension + */ + public String getExtension(); + + /** + * Registers a processor extension with the processor + * + * @param processorExtension the process extension + */ + public void registerProcessorExtension(ProcessorExtension processorExtension); +} diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptImplementation.java b/source/java/org/alfresco/service/cmr/repository/ProcessorExtension.java similarity index 90% rename from source/java/org/alfresco/service/cmr/repository/ScriptImplementation.java rename to source/java/org/alfresco/service/cmr/repository/ProcessorExtension.java index 6d6a46acec..599cf61544 100644 --- a/source/java/org/alfresco/service/cmr/repository/ScriptImplementation.java +++ b/source/java/org/alfresco/service/cmr/repository/ProcessorExtension.java @@ -29,12 +29,12 @@ package org.alfresco.service.cmr.repository; * * @author Roy Wetherall */ -public interface ScriptImplementation +public interface ProcessorExtension { /** - * Returns the name of the script + * Returns the name of the extension * - * @return the name of the script + * @return the name of the extension */ - String getScriptName(); + String getExtensionName(); } diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptException.java b/source/java/org/alfresco/service/cmr/repository/ScriptException.java index d6beec49c1..82b2441371 100644 --- a/source/java/org/alfresco/service/cmr/repository/ScriptException.java +++ b/source/java/org/alfresco/service/cmr/repository/ScriptException.java @@ -31,6 +31,8 @@ import org.alfresco.error.AlfrescoRuntimeException; */ public class ScriptException extends AlfrescoRuntimeException { + private static final long serialVersionUID = 1739480648583299623L; + /** * @param msgId */ diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptProcessor.java b/source/java/org/alfresco/service/cmr/repository/ScriptProcessor.java new file mode 100644 index 0000000000..abfda5cfe0 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/ScriptProcessor.java @@ -0,0 +1,75 @@ +/* + * 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.service.cmr.repository; + +import java.util.Map; + +import org.alfresco.service.namespace.QName; + +/** + * Script processor interface + * + * @author Roy Wetherall + */ +public interface ScriptProcessor extends Processor +{ + /** + * Execute script + * + * @param location the location of the script + * @param model context model + * @return Object the result of the script + */ + public Object execute(ScriptLocation location, Map model); + + /** + * Execute script + * + * @param nodeRef the script node reference + * @param contentProp the content property of the script + * @param model the context model + * @return Object the result of the script + */ + public Object execute(NodeRef nodeRef, QName contentProp, Map model); + + /** + * Execute script + * + * @param location the classpath string locating the script + * @param model the context model + * @return Object the result of the script + */ + public Object execute(String location, Map model); + + /** + * Execute script string + * + * @param script the script string + * @param model the context model + * @return Obejct the result of the script + */ + public Object executeString(String script, Map model); + +} diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptService.java b/source/java/org/alfresco/service/cmr/repository/ScriptService.java index e13fa2619f..b25c69eaf7 100644 --- a/source/java/org/alfresco/service/cmr/repository/ScriptService.java +++ b/source/java/org/alfresco/service/cmr/repository/ScriptService.java @@ -49,7 +49,9 @@ import org.alfresco.service.namespace.QName; public interface ScriptService { /** - * Process a script against the supplied data model. + * Process a script against the supplied data model. + * + * Uses the most approparite script engine or the default if none found. * * @param scriptClasspath Script location as qualified classpath name * @param model Object model to process script against @@ -61,10 +63,28 @@ public interface ScriptService @Auditable(parameters = {"scriptClasspath", "model"}) public Object executeScript(String scriptClasspath, Map model) throws ScriptException; + /** + * Process a script against the supplied data model. + * + * Use the + * + * @param engine the script engine to use + * @param scriptClasspath Script location as qualified classpath name + * @param model Object model to process script against + * + * @return output of the script (may be null or any valid wrapped JavaScript object) + * + * @throws ScriptException + */ + @Auditable(parameters = {"engine", "scriptClasspath", "model"}) + public Object executeScript(String engine, String scriptClasspath, Map model) + throws ScriptException; /** * Process a script against the supplied data model. * + * Uses the most approparite script engine or the default if none found. + * * @param scriptRef Script NodeRef location * @param contentProp QName of the property on the node that contains the content, null can * be passed to indicate the default property of 'cm:content' @@ -81,6 +101,25 @@ public interface ScriptService /** * Process a script against the supplied data model. * + * @param engine the script engine to use + * @param scriptRef Script NodeRef location + * @param contentProp QName of the property on the node that contains the content, null can + * be passed to indicate the default property of 'cm:content' + * @param model Object model to process script against + * + * @return output of the script (may be null or any valid wrapped JavaScript object) + * + * @throws ScriptException + */ + @Auditable(key = Auditable.Key.ARG_1, parameters = {"engine", "scriptRef", "contentProp", "model"}) + public Object executeScript(String engine, NodeRef scriptRef, QName contentProp, Map model) + throws ScriptException; + + /** + * Process a script against the supplied data model + * + * Uses the most approparite script engine or the default if none found. + * * @param scriptLocation object representing the script location * @param model Object model to process script against * @@ -95,6 +134,21 @@ public interface ScriptService /** * Process a script against the supplied data model. * + * @param engine the script engine to use + * @param scriptLocation object representing the script location + * @param model Object model to process script against + * + * @return output of the script (may be null or any other valid wrapped JavaScript object) + * + * @throws ScriptException + */ + @Auditable(parameters = {"engine", "scriptLocation", "model"}) + public Object executeScript(String engine, ScriptLocation scriptLocation, Map model) + throws ScriptException; + + /** + * Process a script against the supplied data model. Uses the default script engine. + * * @param script Script content as a String. * @param model Object model to process script against * @@ -107,10 +161,53 @@ public interface ScriptService throws ScriptException; /** - * Registers a script implementation with the script service + * Process a script against the supplied data model. * - * @param script the script implementation + * @param engine the script engine to use + * @param script Script content as a String. + * @param model Object model to process script against + * + * @return output of the script (may be null or any valid wrapped JavaScript object) + * + * @throws ScriptException */ - @Auditable(parameters = {"script"}) - public void registerScript(ScriptImplementation script); + @Auditable(parameters = {"engine", "script", "model"}) + public Object executeScriptString(String engine, String script, Map model) + throws ScriptException; + + /** + * Registers a script processor with the script service + * + * @param scriptProcessor + */ + @Auditable(parameters = {"scriptProcessot"}) + public void registerScriptProcessor(ScriptProcessor scriptProcessor); + + /** + * Create the default data-model available to scripts as global scope level objects: + *

+ * 'companyhome' - the Company Home node
+ * 'userhome' - the current user home space node
+ * 'person' - the node representing the current user Person
+ * 'script' - the node representing the script itself (may not be available)
+ * 'document' - document context node (may not be available)
+ * 'space' - space context node (may not be available) + * + * @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 Map buildDefaultModel( + NodeRef person, + NodeRef companyHome, + NodeRef userHome, + NodeRef script, + NodeRef document, + NodeRef space); } diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateException.java b/source/java/org/alfresco/service/cmr/repository/TemplateException.java index 941de3ae10..8989e19434 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateException.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateException.java @@ -31,6 +31,8 @@ import org.alfresco.error.AlfrescoRuntimeException; */ public class TemplateException extends AlfrescoRuntimeException { + private static final long serialVersionUID = 2863142603098852564L; + /** * @param msgId */ diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateProcessor.java b/source/java/org/alfresco/service/cmr/repository/TemplateProcessor.java index 025df51f23..5ad3536175 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateProcessor.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateProcessor.java @@ -33,8 +33,8 @@ import java.io.Writer; * * @author Kevin Roast */ -public interface TemplateProcessor -{ +public interface TemplateProcessor extends Processor +{ /** * Process a template against the supplied data model and write to the out. * diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateExtensionImplementation.java b/source/java/org/alfresco/service/cmr/repository/TemplateProcessorExtension.java similarity index 89% rename from source/java/org/alfresco/service/cmr/repository/TemplateExtensionImplementation.java rename to source/java/org/alfresco/service/cmr/repository/TemplateProcessorExtension.java index 92d368172d..aca697130b 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateExtensionImplementation.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateProcessorExtension.java @@ -29,15 +29,8 @@ package org.alfresco.service.cmr.repository; * * @author Kevin Roast */ -public interface TemplateExtensionImplementation -{ - /** - * Returns the name of the template extension - * - * @return the name of the template extension - */ - String getExtensionName(); - +public interface TemplateProcessorExtension extends ProcessorExtension +{ /** * Set the template image resolver for this extension * diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateService.java b/source/java/org/alfresco/service/cmr/repository/TemplateService.java index 5bb2b72b06..03669659bf 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateService.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateService.java @@ -25,7 +25,7 @@ package org.alfresco.service.cmr.repository; import java.io.Writer; -import java.util.List; +import java.util.Map; import org.alfresco.service.Auditable; import org.alfresco.service.PublicService; @@ -47,6 +47,44 @@ import org.alfresco.service.PublicService; @PublicService public interface TemplateService { + /** Keys for default model values */ + public static final String KEY_IMAGE_RESOLVER = "imageresolver"; + public static final String KEY_COMPANY_HOME = "companyhome"; + public static final String KEY_USER_HOME = "userhome"; + public static final String KEY_PERSON = "person"; + public static final String KEY_TEMPLATE = "template"; + public static final String KEY_DATE = "date"; + + /** + * Process a template against the upplied data model and return the result as + * a string. + * + * The template engine used will be determined by the extension of the template. + * + * @param template Template (qualified classpath name or noderef) + * @param model Object model to process template against + * + * @return output of the template process as a String + * @throws TemplateException + */ + @Auditable(parameters = {"template", "model"}) + public String processTemplate(String template, Object model) + throws TemplateException; + + /** + * Process a template against the supplied data model and write to the out. + * + * The template engine used will be determined by the extension of the template. + * + * @param engine Name of the template engine to use + * @param template Template (qualified classpath name or noderef) + * @param model Object model to process template against + * @param out Writer object to send output too + */ + @Auditable(parameters = {"template", "model", "out"}) + public void processTemplate(String template, Object model, Writer out) + throws TemplateException; + /** * Process a template against the supplied data model and write to the out. * @@ -101,7 +139,7 @@ public interface TemplateService @Auditable(parameters = {"engine", "template", "model", "out"}) public void processTemplateString(String engine, String template, Object model, Writer out) throws TemplateException; - + /** * Return a TemplateProcessor instance for the specified engine name. * Note that the processor instance is NOT thread safe! @@ -114,18 +152,28 @@ public interface TemplateService public TemplateProcessor getTemplateProcessor(String engine); /** - * Registers a template extension implementation with the Template service + * Registers a new template processor with the template service * - * @param extension the template extension implementation + * @param templateProcessor the template processor to register */ - @Auditable(parameters = {"extension"}) - public void registerExtension(TemplateExtensionImplementation extension); + @Auditable(parameters = {"templateProcessor"}) + public void registerTemplateProcessor(TemplateProcessor templateProcessor); /** - * Returns a list of the Template Extension objects configured for this service + * Helper method to build a default model * - * @return list of the Template Extension objects configured for this service + * @param person the person node reference + * @param companyHome the company home node refereence + * @param userHome the user home node reference + * @param template the node ref for the template (optional) + * @param imageResolver the image resolver (optional) + * @return */ - @Auditable(warn = true, recordReturnedObject = false) - public List getExtensions(); + @Auditable(parameters = {"person", "compantHome", "userHome", "template", "imageResolver"}) + public Map buildDefaultModel( + NodeRef person, + NodeRef companyHome, + NodeRef userHome, + NodeRef template, + TemplateImageResolver imageResolver); }