diff --git a/config/alfresco/extension/lifecycle-messages.properties b/config/alfresco/extension/lifecycle-messages.properties new file mode 100644 index 0000000000..0d41867744 --- /dev/null +++ b/config/alfresco/extension/lifecycle-messages.properties @@ -0,0 +1,7 @@ +# For Lifecycle Workflow Example +wfl_lifecycleapproval.workflow.title=Lifecycle Review & Approve +wfl_lifecycleapproval.workflow.description=Lifecycle Review & Approval workflow (Auto updates document status) +wfl_lifecycleapproval.node.review.transition.reject.title=Reject +wfl_lifecycleapproval.node.review.transition.reject.description=Reject +wfl_lifecycleapproval.node.review.transition.approve.title=Approve +wfl_lifecycleapproval.node.review.transition.approve.description=Approve \ No newline at end of file diff --git a/config/alfresco/extension/lifecycle-workflow-context.xml.sample b/config/alfresco/extension/lifecycle-workflow-context.xml.sample new file mode 100644 index 0000000000..e2668b7b63 --- /dev/null +++ b/config/alfresco/extension/lifecycle-workflow-context.xml.sample @@ -0,0 +1,29 @@ + + + + + + + + + + jbpm + alfresco/extension/lifecycle_processdefinition.xml + text/xml + false + + + + + + alfresco/extension/lifecycleModel.xml + + + + + alfresco/extension/lifecycle-messages + + + + + diff --git a/config/alfresco/extension/lifecycleModel.xml b/config/alfresco/extension/lifecycleModel.xml new file mode 100644 index 0000000000..e8f6ff8ec3 --- /dev/null +++ b/config/alfresco/extension/lifecycleModel.xml @@ -0,0 +1,53 @@ + + + + + + Workflow Lifecycle Model + + 1.0 + + + + + + + + + + + + + + + + + + Draft + In Review + Approved + + + + + + + + + + Status + + + Status + d:text + Draft + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/extension/lifecycle_processdefinition.xml b/config/alfresco/extension/lifecycle_processdefinition.xml new file mode 100644 index 0000000000..f8683c3d8f --- /dev/null +++ b/config/alfresco/extension/lifecycle_processdefinition.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/extension/workflow-context.xml.sample b/config/alfresco/extension/workflow-context.xml.sample index f1e087c9f3..8b2168359f 100644 --- a/config/alfresco/extension/workflow-context.xml.sample +++ b/config/alfresco/extension/workflow-context.xml.sample @@ -3,7 +3,7 @@ - + @@ -19,6 +19,8 @@ + + @@ -26,6 +28,8 @@ + + diff --git a/config/alfresco/messages/coci-service.properties b/config/alfresco/messages/coci-service.properties index 6f57e97296..112a90b233 100644 --- a/config/alfresco/messages/coci-service.properties +++ b/config/alfresco/messages/coci-service.properties @@ -6,5 +6,5 @@ coci_service.err_not_owner=This user is not the owner of the working copy and ca coci_service.err_workingcopy_checkout=A working copy can not be checked out. coci_service.err_not_authenticated=Can not find the currently authenticated user details required by the CheckOutCheckIn service. coci_service.err_workingcopy_has_no_mimetype=Working copy node ({0}) has no mimetype - +coci_service.err_already_checkedout=This node is already checked out. coci_service.discussion_for={0} discussion \ No newline at end of file diff --git a/config/alfresco/messages/workflow-interpreter-help.txt b/config/alfresco/messages/workflow-interpreter-help.txt index d38c52affb..b6b645911f 100644 --- a/config/alfresco/messages/workflow-interpreter-help.txt +++ b/config/alfresco/messages/workflow-interpreter-help.txt @@ -49,6 +49,15 @@ ok> use definition [] is ommited, the currently selected workflow definition is shown. +ok> undeploy definition + + Undeploy the latest version of the workflow definition identified by + . This will also terminate and remove all "in-flight" + workflows associated with the definition. + + If multiple versions of the definition exist, you will have to undeploy + each in turn to remove the definition completely. + ## ## Variable Commands ## @@ -112,10 +121,11 @@ ok> start []]* start bpm:assignee=david wf:predefined -ok> show workflows +ok> show workflows [all] Display the list of active workflows for the currently selected workflow - definition. + definition. Or, display the list of all workflows when used with additional + keyword 'all'. ok> use workflow @@ -149,6 +159,10 @@ ok> delete workflow Force deletion of the specified . +ok> delete all workflows + + Force deletion of all "in-flight" workflows. Use with care! + ## ## Task Commands ## @@ -176,7 +190,7 @@ ok> update task []]* Update the state of the specified . Task properties are provided as name/value pairs or references to pre-defined variables. - variable name + variable name [*] if specified, define a collection variable value (comma-seperate to specify a list of values) diff --git a/config/alfresco/policy-context.xml b/config/alfresco/policy-context.xml index 0ca2261111..2b49de90b5 100644 --- a/config/alfresco/policy-context.xml +++ b/config/alfresco/policy-context.xml @@ -36,5 +36,11 @@ + + + + + + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 732bcfe5b3..240279078f 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -82,26 +82,6 @@ - - @@ -130,26 +110,6 @@ - - @@ -260,25 +220,6 @@ - - org.alfresco.service.cmr.repository.MimetypeService diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 1ed94603a2..f43f65ead5 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -210,7 +210,13 @@ - + + + ${lucene.query.maxPermissionCheckTimeMillis} + + + ${lucene.query.maxPermissionChecks} + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index a09be156f6..f9e9e31869 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -20,6 +20,14 @@ index.recovery.mode=VALIDATE # Lucene configuration # # #################### # # +# The maximum time spent pruning query results +# +lucene.query.maxPermissionCheckTimeMillis=10000 +# +# The maximum number of results to perform permission checks against +# +lucene.query.maxPermissionChecks=1000 +# # Millisecond threshold for text transformations # Slower transformers will force the text extraction to be asynchronous # diff --git a/config/alfresco/script-services-context.xml b/config/alfresco/script-services-context.xml index 3cc3a465fa..995294635d 100644 --- a/config/alfresco/script-services-context.xml +++ b/config/alfresco/script-services-context.xml @@ -34,7 +34,7 @@ - + diff --git a/config/treecache.xml b/config/treecache.xml deleted file mode 100644 index 708ef2ba71..0000000000 --- a/config/treecache.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - READ_COMMITTED - - - REPL_SYNC - - - Alfresco-Hibernate-Cluster - - - - - - - - - - # - - - - - - - - - - - - - 20000 - - - 10000 - - - 15000 - - - - - - - diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index 338456f2d4..393e9f48a5 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -541,6 +541,65 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( hasPseudoFileInterface(ctx)) { + // Make sure the parent folder has a file state, and the path exists + + String[] paths = FileName.splitPath( path); + FileState fstate = ctx.getStateTable().findFileState( paths[0]); + + if ( fstate == null) + { + NodeRef nodeRef = getNodeForPath(tree, paths[0]); + + if ( nodeRef != null) + { + // Get the file information for the node + + session.beginTransaction(transactionService, true); + finfo = cifsHelper.getFileInformation(nodeRef); + } + + // Create the file state + + fstate = ctx.getStateTable().findFileState( paths[0], true, true); + + fstate.setFileStatus( FileStatus.DirectoryExists); + fstate.setNodeRef( nodeRef); + + // Add pseudo files to the folder + + getPseudoFileInterface( ctx).addPseudoFilesToFolder( session, tree, paths[0]); + + // Debug + + if ( logger.isInfoEnabled()) + logger.info( "Added file state for pseudo files folder (getinfo) - " + paths[0]); + } + else if ( fstate.hasPseudoFiles() == false) + { + // Make sure the file state has the node ref + + if ( fstate.hasNodeRef() == false) + { + // Create the transaction + + session.beginTransaction(transactionService, true); + + // Get the node for the folder path + + fstate.setNodeRef( getNodeForPath( tree, paths[0])); + } + + // Add pseudo files for the parent folder + + getPseudoFileInterface( ctx).addPseudoFilesToFolder( session, tree, paths[0]); + + // Debug + + if ( logger.isInfoEnabled()) + logger.info( "Added pseudo files for folder (exists) - " + paths[0]); + } + + // Get the pseudo file PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( session, tree, path); @@ -868,6 +927,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Check if the file name is a pseudo file name if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, name)) { + // Make sure the parent folder has a file state, and the path exists String[] paths = FileName.splitPath( name); diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index ae430ef149..c4911ecb84 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -57,6 +57,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService private static final String MSG_ERR_ALREADY_WORKING_COPY = "coci_service.err_workingcopy_checkout"; private static final String MSG_ERR_NOT_AUTHENTICATED = "coci_service.err_not_authenticated"; private static final String MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE = "coci_service.err_workingcopy_has_no_mimetype"; + private static final String MSG_ALREADY_CHECKEDOUT = "coci_service.err_already_checkedout"; /** * Extension character, used to recalculate the working copy names @@ -189,6 +190,12 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService QName destinationAssocTypeQName, QName destinationAssocQName) { + LockType lockType = this.lockService.getLockType(nodeRef); + if (LockType.READ_ONLY_LOCK.equals(lockType) == true) + { + throw new CheckOutCheckInServiceException(MSG_ALREADY_CHECKEDOUT); + } + // Make sure we are no checking out a working copy node if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == true) { diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java index c8f4edc937..24aa04cef4 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java @@ -430,5 +430,32 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest NodeRef wk3 = this.cociService.getWorkingCopy(this.nodeRef); assertNull(wk3); } + + public void testAR1056() + { + // Check out the node + NodeRef workingCopy = this.cociService.checkout( + this.nodeRef, + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}workingCopy")); + assertNotNull(workingCopy); + + // Try and check the same node out again + try + { + this.cociService.checkout( + this.nodeRef, + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}workingCopy2")); + fail("This document has been checked out twice."); + } + catch (Exception exception) + { + // Good because we shouldnt be able to checkout a document twice + } + + } } diff --git a/source/java/org/alfresco/repo/jscript/Association.java b/source/java/org/alfresco/repo/jscript/Association.java new file mode 100644 index 0000000000..89d933e7e7 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/Association.java @@ -0,0 +1,91 @@ +/** + * + */ +package org.alfresco.repo.jscript; + +import java.io.Serializable; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.util.ParameterCheck; +import org.mozilla.javascript.Scriptable; + +/** + * Object representing an association + * + * @author Roy Wetherall + */ +public class Association implements Scopeable, Serializable +{ + /** Serial version UUID*/ + private static final long serialVersionUID = 897788515655487131L; + + /** Service registry **/ + private ServiceRegistry services; + + /** Script scope **/ + private Scriptable scope; + + /** Association reference **/ + private AssociationRef assocRef; + + public Association(ServiceRegistry services, AssociationRef assocRef) + { + this(services, assocRef, null); + } + + public Association(ServiceRegistry services, AssociationRef assocRef, Scriptable scope) + { + ParameterCheck.mandatory("Service registry", services); + ParameterCheck.mandatory("Association reference", assocRef); + this.services = services; + this.assocRef = assocRef; + if (scope != null) + { + this.scope = scope; + } + } + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + public AssociationRef getAssociationRef() + { + return this.assocRef; + } + + public String getType() + { + return assocRef.getTypeQName().toString(); + } + + public String jsGet_type() + { + return getType(); + } + + public Node getSource() + { + return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, assocRef.getSourceRef()); + } + + public Node jsGet_source() + { + return getSource(); + } + + public Node getTarget() + { + return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, assocRef.getTargetRef()); + } + + public Node jsGet_target() + { + return getTarget(); + } +} diff --git a/source/java/org/alfresco/repo/jscript/Behaviour.java b/source/java/org/alfresco/repo/jscript/Behaviour.java new file mode 100644 index 0000000000..b1742bab97 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/Behaviour.java @@ -0,0 +1,108 @@ +/** + * + */ +package org.alfresco.repo.jscript; + +import java.io.Serializable; + +import org.alfresco.service.ServiceRegistry; +import org.mozilla.javascript.Scriptable; + +/** + * Object representing the behaviour information + * + * @author Roy Wetherall + */ +public class Behaviour implements Scopeable, Serializable +{ + /** Serial version UID **/ + private static final long serialVersionUID = 1936017361886646100L; + + /** Service registry **/ + private ServiceRegistry services; + + /** Script scope **/ + private Scriptable scope; + + /** The name of the policy that this behaviour is linked to **/ + private String name; + + /** The behaviour argument values **/ + private Object[] args; + + /** Cached js converted argument values **/ + private Serializable[] jsArgs; + + /** + * Constructor + * + * @param services the service registry + * @param name the name of the policy associated with this behaviour + * @param args the argument values + */ + public Behaviour(ServiceRegistry services, String name, Object[] args) + { + this.services = services; + this.name = name; + this.args = args; + } + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + /** + * Get the policy name + * + * @return the name of the policy + */ + public String getName() + { + return this.name; + } + + /** + * JS accessor method + * + * @return the name of the policy + */ + public String jsGet_name() + { + return getName(); + } + + /** + * The argument values + * + * @return array containing the argument values + */ + public Serializable[] getArgs() + { + if (this.jsArgs == null) + { + ValueConverter valueConverter = new ValueConverter(); + this.jsArgs = new Serializable[args.length]; + int index = 0; + for (Object arg : this.args) + { + this.jsArgs[index] = valueConverter.convertValueForScript(services, this.scope, null, (Serializable)arg); + index ++; + } + } + return this.jsArgs; + } + + /** + * JS accessor method + * + * @return array containing the argument values + */ + public Serializable[] jsGet_args() + { + return getArgs(); + } +} diff --git a/source/java/org/alfresco/repo/jscript/ChildAssociation.java b/source/java/org/alfresco/repo/jscript/ChildAssociation.java new file mode 100644 index 0000000000..6dbcb0a13d --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/ChildAssociation.java @@ -0,0 +1,122 @@ +/** + * + */ +package org.alfresco.repo.jscript; + +import java.io.Serializable; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.util.ParameterCheck; +import org.mozilla.javascript.Scriptable; + +/** + * Object representing a child association + * + * @author Roy Wetherall + */ +public class ChildAssociation implements Scopeable, Serializable +{ + /** Serial version UUID **/ + private static final long serialVersionUID = -2122640697340663213L; + + /** Service registry **/ + private ServiceRegistry services; + + /** Script scope **/ + private Scriptable scope; + + /** Child association reference **/ + private ChildAssociationRef childAssocRef; + + public ChildAssociation(ServiceRegistry services, ChildAssociationRef childAssocRef) + { + this(services, childAssocRef, null); + } + + /** + * Constructor + * + * @param services + * @param childAssocRef + */ + public ChildAssociation(ServiceRegistry services, ChildAssociationRef childAssocRef, Scriptable scope) + { + ParameterCheck.mandatory("Service registry", services); + ParameterCheck.mandatory("Child association reference", childAssocRef); + this.services = services; + this.childAssocRef = childAssocRef; + if (scope != null) + { + this.scope = scope; + } + } + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + public ChildAssociationRef getChildAssociationRef() + { + return this.childAssocRef; + } + + public String getType() + { + return childAssocRef.getTypeQName().toString(); + } + + public String jsGet_type() + { + return getType(); + } + + public String getName() + { + return childAssocRef.getQName().toString(); + } + + public String jsGet_name() + { + return getName(); + } + + public Node getParent() + { + return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, childAssocRef.getParentRef()); + } + + public Node jsGet_parent() + { + return getParent(); + } + + public Node getChild() + { + return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, childAssocRef.getChildRef()); + } + + public Node jsGet_child() + { + return getChild(); + } + + public boolean isPrimary() + { + return this.childAssocRef.isPrimary(); + } + + public int getNthSibling() + { + return this.childAssocRef.getNthSibling(); + } + + public int jsGet_nthSibling() + { + return getNthSibling(); + } +} diff --git a/source/java/org/alfresco/repo/jscript/ClasspathScriptLocation.java b/source/java/org/alfresco/repo/jscript/ClasspathScriptLocation.java new file mode 100644 index 0000000000..d6a007f03a --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/ClasspathScriptLocation.java @@ -0,0 +1,87 @@ +/** + * + */ +package org.alfresco.repo.jscript; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.ScriptException; +import org.alfresco.service.cmr.repository.ScriptLocation; +import org.alfresco.util.ParameterCheck; + +/** + * Classpath script location object. + * + * @author Roy Wetherall + * + */ +public class ClasspathScriptLocation implements ScriptLocation +{ + /** Classpath location **/ + private String location; + + /** + * Constructor + * + * @param location the classpath location + */ + public ClasspathScriptLocation(String location) + { + ParameterCheck.mandatory("Location", location); + this.location = location; + } + + /** + * @see org.alfresco.service.cmr.repository.ScriptLocation#getReader() + */ + public Reader getReader() + { + Reader reader = null; + try + { + InputStream stream = getClass().getClassLoader().getResourceAsStream(location); + if (stream == null) + { + throw new AlfrescoRuntimeException("Unable to load classpath resource: " + location); + } + reader = new InputStreamReader(stream); + } + catch (Throwable err) + { + throw new ScriptException("Failed to load classpath resource '" + location + "': " + err.getMessage(), err); + } + + return reader; + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + else if (obj == null || !(obj instanceof ClasspathScriptLocation)) + { + return false; + } + ClasspathScriptLocation other = (ClasspathScriptLocation)obj; + return this.location.equals(other.location); + } + + @Override + public int hashCode() + { + return 37 * this.location.hashCode(); + } + + @Override + public String toString() + { + return this.location.toString(); + } + +} diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java index 400e610a81..81a4b5934d 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java @@ -33,9 +33,11 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.ScriptException; import org.alfresco.service.cmr.repository.ScriptImplementation; +import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.TemplateImageResolver; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; import org.apache.log4j.Logger; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; @@ -165,6 +167,37 @@ public class RhinoScriptService implements ScriptService } } } + + /** + * @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()); + } + + Reader reader = null; + try + { + return executeScriptImpl(location.getReader(), model); + } + catch (Throwable err) + { + throw new ScriptException("Failed to execute script '" + location.toString() + "': " + err.getMessage(), err); + } + finally + { + if (reader != null) + { + try {reader.close();} catch (IOException ioErr) {} + } + } + } /** * @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map) diff --git a/source/java/org/alfresco/repo/jscript/ScriptBehaviour.java b/source/java/org/alfresco/repo/jscript/ScriptBehaviour.java new file mode 100644 index 0000000000..bfbd3cd5bb --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/ScriptBehaviour.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.jscript; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.repo.policy.BaseBehaviour; +import org.alfresco.repo.policy.PolicyException; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ScriptLocation; +import org.alfresco.util.ParameterCheck; + + +/** + * JavaScript behaviour implementation + * + * @author Roy Wetherall + */ +public class ScriptBehaviour extends BaseBehaviour +{ + private ServiceRegistry serviceRegistry; + + private ScriptLocation location; + + public ScriptBehaviour() + { + super(); + } + + public ScriptBehaviour(ServiceRegistry serviceRegistry, ScriptLocation location) + { + this(serviceRegistry, location, NotificationFrequency.EVERY_EVENT); + } + + public ScriptBehaviour(ServiceRegistry serviceRegistry, ScriptLocation location, NotificationFrequency frequency) + { + super(frequency); + ParameterCheck.mandatory("Location", location); + ParameterCheck.mandatory("ServiceRegistry", serviceRegistry); + this.location = location; + this.serviceRegistry = serviceRegistry; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + public void setLocation(ScriptLocation location) + { + this.location = location; + } + + + @Override + public String toString() + { + return "JavaScript behaviour[location = " + this.location.toString() + "]"; + } + + @SuppressWarnings("unchecked") + public synchronized T getInterface(Class policy) + { + ParameterCheck.mandatory("Policy class", policy); + Object proxy = proxies.get(policy); + if (proxy == null) + { + Method[] policyIFMethods = policy.getMethods(); + if (policyIFMethods.length != 1) + { + throw new PolicyException("Policy interface " + policy.getCanonicalName() + " must have only one method"); + } + + InvocationHandler handler = new JavaScriptInvocationHandler(this); + proxy = Proxy.newProxyInstance(policy.getClassLoader(), new Class[]{policy}, handler); + proxies.put(policy, proxy); + } + return (T)proxy; + } + + /** + * JavaScript Invocation Handler + * + * @author Roy Wetherall + */ + private static class JavaScriptInvocationHandler implements InvocationHandler + { + private ScriptBehaviour behaviour; + + private JavaScriptInvocationHandler(ScriptBehaviour behaviour) + { + this.behaviour = behaviour; + } + + /** + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + // Handle Object level methods + if (method.getName().equals("toString")) + { + return toString(); + } + else if (method.getName().equals("hashCode")) + { + return hashCode(); + } + else if (method.getName().equals("equals")) + { + if (Proxy.isProxyClass(args[0].getClass())) + { + return equals(Proxy.getInvocationHandler(args[0])); + } + return false; + } + + // Delegate to designated method pointer + if (behaviour.isEnabled()) + { + try + { + behaviour.disable(); + return invokeScript(method, args); + } + finally + { + behaviour.enable(); + } + } + return null; + } + + private Object invokeScript(Method method, Object[] args) + { + // Build the model + Map model = new HashMap(1); + model.put("behaviour", new org.alfresco.repo.jscript.Behaviour(this.behaviour.serviceRegistry, method.getName(), args)); + + // Execute the script + return this.behaviour.serviceRegistry.getScriptService().executeScript(this.behaviour.location, model); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + else if (obj == null || !(obj instanceof JavaScriptInvocationHandler)) + { + return false; + } + JavaScriptInvocationHandler other = (JavaScriptInvocationHandler)obj; + return behaviour.location.equals(other.behaviour.location); + } + + @Override + public int hashCode() + { + return 37 * behaviour.location.hashCode(); + } + + @Override + public String toString() + { + return "JavaScriptBehaviour[location=" + behaviour.location.toString() + "]"; + } + } +} diff --git a/source/java/org/alfresco/repo/jscript/ScriptBehaviourTest.java b/source/java/org/alfresco/repo/jscript/ScriptBehaviourTest.java new file mode 100644 index 0000000000..c0b616575f --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/ScriptBehaviourTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.jscript; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptLocation; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.BaseSpringTest; + +/** + * + * + * @author Roy Wetherall + */ +public class ScriptBehaviourTest extends BaseSpringTest +{ + private ServiceRegistry serviceRegistry; + private NodeService nodeService; + private PolicyComponent policyComponent; + + private StoreRef storeRef; + private NodeRef folderNodeRef; + + protected String[] getConfigLocations() + { + return new String[] { "classpath:org/alfresco/repo/jscript/test-context.xml" }; + } + + /** + * On setup in transaction implementation + */ + @Override + protected void onSetUpInTransaction() + throws Exception + { + // Get the required services + this.nodeService = (NodeService)this.applicationContext.getBean("nodeService"); + this.policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent"); + this.serviceRegistry = (ServiceRegistry)this.applicationContext.getBean("ServiceRegistry"); + + AuthenticationComponent authenticationComponent = (AuthenticationComponent)this.applicationContext.getBean("authenticationComponent"); + authenticationComponent.setCurrentUser("admin"); + + // Create the store and get the root node reference + this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + NodeRef rootNodeRef = this.nodeService.getRootNode(storeRef); + + // Create folder node + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "TestFolder"); + ChildAssociationRef childAssocRef = this.nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}TestFolder"), + ContentModel.TYPE_FOLDER, + props); + this.folderNodeRef = childAssocRef.getChildRef(); + } + + public void testEnableDiableBehaviour() + { + // Register the onCreateNode behaviour script + ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/jscript/test_onCreateNode_cmContent.js"); + ScriptBehaviour behaviour = new ScriptBehaviour(this.serviceRegistry, location); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NodeServicePolicies.OnCreateNodePolicy.NAMESPACE, "onCreateNode"), + ContentModel.TYPE_CONTENT, + behaviour); + + behaviour.disable(); + + // Create a content node + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "myDoc.txt"); + ChildAssociationRef childAssoc = this.nodeService.createNode( + this.folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc.txt"), + ContentModel.TYPE_CONTENT, + props); + assertFalse(this.nodeService.hasAspect(childAssoc.getChildRef(), ContentModel.ASPECT_TITLED)); + + behaviour.enable(); + + Map props2 = new HashMap(1); + props2.put(ContentModel.PROP_NAME, "myDoc1.txt"); + ChildAssociationRef childAssoc2 = this.nodeService.createNode( + this.folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc1.txt"), + ContentModel.TYPE_CONTENT, + props2); + assertTrue(this.nodeService.hasAspect(childAssoc2.getChildRef(), ContentModel.ASPECT_TITLED)); + } + + public void testClasspathLocationBehaviour() + { + // Register the onCreateNode behaviour script + ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/jscript/test_onCreateNode_cmContent.js"); + ScriptBehaviour behaviour = new ScriptBehaviour(this.serviceRegistry, location); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NodeServicePolicies.OnCreateNodePolicy.NAMESPACE, "onCreateNode"), + ContentModel.TYPE_CONTENT, + behaviour); + + // Create a content node + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "myDoc.txt"); + ChildAssociationRef childAssoc = this.nodeService.createNode( + this.folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc.txt"), + ContentModel.TYPE_CONTENT, + props); + + // Since the behavoiour will have been run check that the titled aspect has been applied + assertTrue(this.nodeService.hasAspect(childAssoc.getChildRef(), ContentModel.ASPECT_TITLED)); + } + + public void testSpringConfiguredBehaviour() + { + this.nodeService.addAspect(this.folderNodeRef, ContentModel.ASPECT_COUNTABLE, null); + assertTrue(this.nodeService.hasAspect(this.folderNodeRef, ContentModel.ASPECT_TITLED)); + + // Create a couple of nodes + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "myDoc.txt"); + ChildAssociationRef childAssoc = this.nodeService.createNode( + this.folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc.txt"), + ContentModel.TYPE_CONTENT, + props); + Map props2 = new HashMap(1); + props2.put(ContentModel.PROP_NAME, "folder2"); + ChildAssociationRef childAssoc2 = this.nodeService.createNode( + this.folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "folder2"), + ContentModel.TYPE_FOLDER, + props2); + + this.nodeService.addChild(childAssoc2.getChildRef(), childAssoc.getChildRef(), ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "linked")); + assertTrue(this.nodeService.hasAspect(childAssoc.getChildRef(), ContentModel.ASPECT_VERSIONABLE)); + } +} diff --git a/source/java/org/alfresco/repo/jscript/Search.java b/source/java/org/alfresco/repo/jscript/Search.java index 79ee477ae5..2497b6e122 100644 --- a/source/java/org/alfresco/repo/jscript/Search.java +++ b/source/java/org/alfresco/repo/jscript/Search.java @@ -20,6 +20,7 @@ import java.io.StringReader; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.NodeRef; @@ -101,9 +102,8 @@ public final class Search implements Scopeable */ public Node findNode(String ref) { - String query = ref.replace(":", "\\:"); - query = query.replace("/", "\\/"); - Node[] result = query("ID:" + query); + String query = "ID:" + LuceneQueryParser.escape(ref); + Node[] result = query(query); if (result.length == 1) { return result[0]; diff --git a/source/java/org/alfresco/repo/jscript/ValueConverter.java b/source/java/org/alfresco/repo/jscript/ValueConverter.java index 9fb35452c9..d535758d19 100644 --- a/source/java/org/alfresco/repo/jscript/ValueConverter.java +++ b/source/java/org/alfresco/repo/jscript/ValueConverter.java @@ -23,7 +23,10 @@ import java.util.Date; import java.util.List; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.mozilla.javascript.Context; import org.mozilla.javascript.NativeArray; @@ -38,7 +41,6 @@ import org.mozilla.javascript.Wrapper; */ public class ValueConverter { - /** * Convert an object from any repository serialized value to a valid script object. * This includes converting Collection multi-value properties into JavaScript Array objects. @@ -63,6 +65,18 @@ public class ValueConverter // so they can be used as objects within a template value = new Node(((NodeRef)value), services, null, scope); } + else if (value instanceof QName || value instanceof StoreRef) + { + value = value.toString(); + } + else if (value instanceof ChildAssociationRef) + { + value = new ChildAssociation(services, (ChildAssociationRef)value, scope); + } + else if (value instanceof AssociationRef) + { + value = new Association(services, (AssociationRef)value, scope); + } else if (value instanceof Date) { // convert Date to JavaScript native Date object @@ -110,6 +124,14 @@ public class ValueConverter // convert back to NodeRef value = ((Node)value).getNodeRef(); } + else if (value instanceof ChildAssociation) + { + value = ((ChildAssociation)value).getChildAssociationRef(); + } + else if (value instanceof Association) + { + value = ((Association)value).getAssociationRef(); + } else if (value instanceof Wrapper) { // unwrap a Java object from a JavaScript wrapper diff --git a/source/java/org/alfresco/repo/jscript/test-context.xml b/source/java/org/alfresco/repo/jscript/test-context.xml new file mode 100644 index 0000000000..4a0effc362 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/test-context.xml @@ -0,0 +1,51 @@ + + + + + + + + + + {http://www.alfresco.org}onAddAspect + + + {http://www.alfresco.org/model/content/1.0}countable + + + + + + + org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js + + + + + + + + + + {http://www.alfresco.org}onCreateChildAssociation + + + {http://www.alfresco.org/model/content/1.0}folder + + + {http://www.alfresco.org/model/content/1.0}contains + + + + + + + org/alfresco/repo/jscript/test_onCreateChildAssociation.js + + + + + + + + diff --git a/source/java/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js b/source/java/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js new file mode 100644 index 0000000000..32e6309181 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js @@ -0,0 +1,55 @@ +logger.log("The counatble aspect has been added"); + +var scriptFailed = false; + +// Have a look at the behaviour object that should have been passed +if (behaviour == null) +{ + logger.log("The behaviour object has not been set."); + scriptFailed = true; +} + +// Check the name of the behaviour +if (behaviour.name == null && behaviour.name != "onAddAspect") +{ + logger.log("The behaviour name has not been set correctly."); + scriptFailed = true; +} +else +{ + logger.log("Behaviour name: " + behaviour.name); +} + +// Check the arguments +if (behaviour.args == null) +{ + logger.log("The args have not been set.") + scriptFailed = true; +} +else +{ + if (behaviour.args.length == 2) + { + var nodeRef = behaviour.args[0]; + var aspectType = behaviour.args[1]; + logger.log("NodeRef: " + nodeRef.id); + logger.log("Type: " + aspectType); + if (aspectType != "{http://www.alfresco.org/model/content/1.0}countable") + { + logger.log("Aspect type is incorrect"); + scriptFailed = true; + } + } + else + { + logger.log("The number of arguments is incorrect.") + scriptFailed = true; + } +} + +if (scriptFailed == false) +{ + nodeRef.addAspect("cm:titled"); + nodeRef.save(); +} + diff --git a/source/java/org/alfresco/repo/jscript/test_onCreateChildAssociation.js b/source/java/org/alfresco/repo/jscript/test_onCreateChildAssociation.js new file mode 100644 index 0000000000..61b1aa22a9 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/test_onCreateChildAssociation.js @@ -0,0 +1,51 @@ +var scriptFailed = false; + +// Have a look at the behaviour object that should have been passed +if (behaviour == null) +{ + logger.log("The behaviour object has not been set."); + scriptFailed = true; +} + +// Check the name of the behaviour +if (behaviour.name == null && behaviour.name != "onCreateChildAssociation") +{ + logger.log("The behaviour name has not been set correctly."); + scriptFailed = true; +} +else +{ + logger.log("Behaviour name: " + behaviour.name); +} + +// Check the arguments +if (behaviour.args == null) +{ + logger.log("The args have not been set.") + scriptFailed = true; +} +else +{ + if (behaviour.args.length == 1) + { + var childAssoc = behaviour.args[0]; + logger.log("Assoc type: " + childAssoc.type); + logger.log("Assoc name: " + childAssoc.name); + logger.log("Parent node: " + childAssoc.parent.id); + logger.log("Child node: " + childAssoc.child.id); + logger.log("Is primary: " + childAssoc.isPrimary()); + logger.log("Nth sibling: " + childAssoc.nthSibling); + } + else + { + logger.log("The number of arguments is incorrect.") + scriptFailed = true; + } +} + +if (scriptFailed == false) +{ + childAssoc.child.addAspect("cm:versionable"); + childAssoc.child.save(); +} + diff --git a/source/java/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js b/source/java/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js new file mode 100644 index 0000000000..1d7d1baf62 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js @@ -0,0 +1,51 @@ +var scriptFailed = false; + +// Have a look at the behaviour object that should have been passed +if (behaviour == null) +{ + logger.log("The behaviour object has not been set."); + scriptFailed = true; +} + +// Check the name of the behaviour +if (behaviour.name == null && behaviour.name != "onCreateNode") +{ + logger.log("The behaviour name has not been set correctly."); + scriptFailed = true; +} +else +{ + logger.log("Behaviour name: " + behaviour.name); +} + +// Check the arguments +if (behaviour.args == null) +{ + logger.log("The args have not been set.") + scriptFailed = true; +} +else +{ + if (behaviour.args.length == 1) + { + var childAssoc = behaviour.args[0]; + logger.log("Assoc type: " + childAssoc.type); + logger.log("Assoc name: " + childAssoc.name); + logger.log("Parent node: " + childAssoc.parent.id); + logger.log("Child node: " + childAssoc.child.id); + logger.log("Is primary: " + childAssoc.isPrimary()); + logger.log("Nth sibling: " + childAssoc.nthSibling); + } + else + { + logger.log("The number of arguments is incorrect.") + scriptFailed = true; + } +} + +if (scriptFailed == false) +{ + childAssoc.child.addAspect("cm:titled"); + childAssoc.child.save(); +} + diff --git a/source/java/org/alfresco/repo/policy/BaseBehaviour.java b/source/java/org/alfresco/repo/policy/BaseBehaviour.java new file mode 100644 index 0000000000..406f89d810 --- /dev/null +++ b/source/java/org/alfresco/repo/policy/BaseBehaviour.java @@ -0,0 +1,109 @@ +/** + * + */ +package org.alfresco.repo.policy; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.util.ParameterCheck; + +/** + * Base behaviour implementation + * + * @author Roy Wetherall + */ +public abstract class BaseBehaviour implements Behaviour +{ + /** The notification frequency */ + protected NotificationFrequency frequency = NotificationFrequency.EVERY_EVENT; + + /** Disabled stack **/ + private StackThreadLocal disabled = new StackThreadLocal(); + + /** Proxies **/ + protected Map proxies = new HashMap(); + + /** + * Default constructor + */ + public BaseBehaviour() + { + // Default constructor + } + + /** + * Constructor + * + * @param frequency the notification frequency + */ + public BaseBehaviour(NotificationFrequency frequency) + { + ParameterCheck.mandatory("Frequency", frequency); + this.frequency = frequency; + } + + public void setNotificationFrequency(NotificationFrequency frequency) + { + this.frequency = frequency; + } + + /** + * Disable this behaviour for the curent thread + */ + public void disable() + { + Stack stack = disabled.get(); + stack.push(hashCode()); + } + + /** + * Enable this behaviour for the current thread + */ + public void enable() + { + Stack stack = disabled.get(); + if (stack.peek().equals(hashCode()) == false) + { + throw new PolicyException("Cannot enable " + this.toString() + " at this time - mismatched with disable calls"); + } + stack.pop(); + } + + /** + * Indicates whether the this behaviour is current enabled or not + * + * @return true if the behaviour is enabled, false otherwise + */ + public boolean isEnabled() + { + Stack stack = disabled.get(); + return stack.search(hashCode()) == -1; + } + + /** + * Get the notification frequency + * + * @return the notification frequency + */ + public NotificationFrequency getNotificationFrequency() + { + return frequency; + } + + /** + * Stack specific Thread Local + * + * @author David Caruana + */ + class StackThreadLocal extends ThreadLocal> + { + @Override + protected Stack initialValue() + { + return new Stack(); + } + } +} diff --git a/source/java/org/alfresco/repo/policy/JavaBehaviour.java b/source/java/org/alfresco/repo/policy/JavaBehaviour.java index 6adc0a90b2..274d5cb0dc 100644 --- a/source/java/org/alfresco/repo/policy/JavaBehaviour.java +++ b/source/java/org/alfresco/repo/policy/JavaBehaviour.java @@ -20,9 +20,6 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.util.HashMap; -import java.util.Map; -import java.util.Stack; import org.alfresco.util.ParameterCheck; @@ -36,24 +33,14 @@ import org.alfresco.util.ParameterCheck; * @author David Caruana * */ -public class JavaBehaviour implements Behaviour +public class JavaBehaviour extends BaseBehaviour { // The object instance holding the method - private Object instance; + Object instance; // The method name - private String method; + String method; - // Notification Frequency - private NotificationFrequency frequency; - - // Cache of interface proxies (by interface class) - private Map proxies = new HashMap(); - - // Enable / Disable invocation of behaviour - private StackThreadLocal disabled = new StackThreadLocal(); - - /** * Construct. * @@ -73,76 +60,33 @@ public class JavaBehaviour implements Behaviour */ public JavaBehaviour(Object instance, String method, NotificationFrequency frequency) { - ParameterCheck.mandatory("Instance", instance); + super(frequency); + ParameterCheck.mandatory("Instance", instance); ParameterCheck.mandatory("Method", method); - this.instance = instance; this.method = method; - this.frequency = frequency; + this.instance = instance; } - /* (non-Javadoc) - * @see org.alfresco.repo.policy.Behaviour#getInterface(java.lang.Class) - */ - @SuppressWarnings("unchecked") - public synchronized T getInterface(Class policy) - { - ParameterCheck.mandatory("Policy class", policy); - Object proxy = proxies.get(policy); - if (proxy == null) - { - InvocationHandler handler = getInvocationHandler(instance, method, policy); - proxy = Proxy.newProxyInstance(policy.getClassLoader(), new Class[]{policy}, handler); - proxies.put(policy, proxy); - } - return (T)proxy; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.policy.Behaviour#disable() - */ - public void disable() - { - Stack stack = disabled.get(); - stack.push(hashCode()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.policy.Behaviour#enable() - */ - public void enable() - { - Stack stack = disabled.get(); - if (stack.peek().equals(hashCode()) == false) - { - throw new PolicyException("Cannot enable " + this.toString() + " at this time - mismatched with disable calls"); - } - stack.pop(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.policy.Behaviour#isEnabled() - */ - public boolean isEnabled() - { - Stack stack = disabled.get(); - return stack.search(hashCode()) == -1; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.policy.Behaviour#getNotificationFrequency() - */ - public NotificationFrequency getNotificationFrequency() - { - return frequency; - } - - @Override public String toString() { return "Java method[class=" + instance.getClass().getName() + ", method=" + method + "]"; } + + @SuppressWarnings("unchecked") + public synchronized T getInterface(Class policy) + { + ParameterCheck.mandatory("Policy class", policy); + Object proxy = proxies.get(policy); + if (proxy == null) + { + InvocationHandler handler = getInvocationHandler(instance, method, policy); + proxy = Proxy.newProxyInstance(policy.getClassLoader(), new Class[]{policy}, handler); + proxies.put(policy, proxy); + } + return (T)proxy; + } /** * Gets the Invocation Handler. @@ -153,7 +97,7 @@ public class JavaBehaviour implements Behaviour * @param policyIF the policy interface class * @return the invocation handler */ - private InvocationHandler getInvocationHandler(Object instance, String method, Class policyIF) + InvocationHandler getInvocationHandler(Object instance, String method, Class policyIF) { Method[] policyIFMethods = policyIF.getMethods(); if (policyIFMethods.length != 1) @@ -171,23 +115,7 @@ public class JavaBehaviour implements Behaviour { throw new PolicyException("Method " + method + " not found or accessible on " + instance.getClass(), e); } - } - - - /** - * Stack specific Thread Local - * - * @author David Caruana - */ - private class StackThreadLocal extends ThreadLocal> - { - @Override - protected Stack initialValue() - { - return new Stack(); - } - } - + } /** * Java Method Invocation Handler diff --git a/source/java/org/alfresco/repo/policy/registration/AssociationPolicyRegistration.java b/source/java/org/alfresco/repo/policy/registration/AssociationPolicyRegistration.java new file mode 100644 index 0000000000..31e1cfc88f --- /dev/null +++ b/source/java/org/alfresco/repo/policy/registration/AssociationPolicyRegistration.java @@ -0,0 +1,46 @@ +/** + * + */ +package org.alfresco.repo.policy.registration; + +import org.alfresco.service.namespace.QName; + +/** + * Deals with the registration of an association policy + * + * @author Roy Wetherall + * + */ +public class AssociationPolicyRegistration extends PolicyRegistration +{ + /** The association type **/ + private QName associationType; + + /** + * Set the association type + * + * @param associationType the association type + */ + public void setAssociationType(String associationType) + { + this.associationType = QName.createQName(associationType); + } + + /** + * @see org.alfresco.repo.policy.registration.PolicyRegistration#register() + */ + @Override + public void register() + { + // Register the association behaviour + if (this.associationType == null) + { + this.policyComponent.bindAssociationBehaviour(this.policyName, this.className, this.behaviour); + } + else + { + this.policyComponent.bindAssociationBehaviour(this.policyName, this.className, this.associationType, this.behaviour); + } + } + +} diff --git a/source/java/org/alfresco/repo/policy/registration/ClassPolicyRegistration.java b/source/java/org/alfresco/repo/policy/registration/ClassPolicyRegistration.java new file mode 100644 index 0000000000..c21cf5eee0 --- /dev/null +++ b/source/java/org/alfresco/repo/policy/registration/ClassPolicyRegistration.java @@ -0,0 +1,24 @@ +/** + * + */ +package org.alfresco.repo.policy.registration; + +/** + * Deal with the registration of a class policy + * + * @author Roy Wetherall + * + */ +public class ClassPolicyRegistration extends PolicyRegistration +{ + /** + * @see org.alfresco.repo.policy.registration.PolicyRegistration#register() + */ + @Override + public void register() + { + // Register the class behaviour + this.policyComponent.bindClassBehaviour(this.policyName, this.className, this.behaviour); + } + +} diff --git a/source/java/org/alfresco/repo/policy/registration/PolicyRegistration.java b/source/java/org/alfresco/repo/policy/registration/PolicyRegistration.java new file mode 100644 index 0000000000..89eda6c7ce --- /dev/null +++ b/source/java/org/alfresco/repo/policy/registration/PolicyRegistration.java @@ -0,0 +1,76 @@ +/** + * + */ +package org.alfresco.repo.policy.registration; + +import org.alfresco.repo.policy.Behaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.namespace.QName; + +/** + * Bean that can be configured in spring to register a policy bahaviour + * + * @author Roy Wetherall + */ +public abstract class PolicyRegistration +{ + /** The policy componenet **/ + protected PolicyComponent policyComponent; + + /** The policy name **/ + protected QName policyName; + + /** The class name **/ + protected QName className; + + /** The behaviour **/ + protected Behaviour behaviour; + + /** + * Set the policy component + * + * @param policyComponent the policy componenet + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the policy name + * + * @param policyName the policy name + */ + public void setPolicyName(String policyName) + { + this.policyName = QName.createQName(policyName); + } + + /** + * Set the class name + * + * @param className the class name + */ + public void setClassName(String className) + { + this.className = QName.createQName(className); + } + + /** + * Set the behaviour + * + * @param behaviour the behaviour + */ + public void setBehaviour(Behaviour behaviour) + { + this.behaviour = behaviour; + } + + /** + * Registers the behaviour with the policy component for the policy and type specified. Called + * as the init method of the bean. + * + * TODO supoort service registration? + */ + public abstract void register(); +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java index 3d4c7be65e..a8bcf23e62 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java @@ -259,8 +259,13 @@ public class LuceneSearcherImpl2 extends LuceneBase2 implements LuceneSearcher2 hits = searcher.search(query); } - return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray( - new Path[0]), searchParameters); + Path[] paths = searchParameters.getAttributePaths().toArray(new Path[0]); + return new LuceneResultSet( + hits, + searcher, + nodeService, + paths, + searchParameters); } catch (ParseException e) diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java index 83a3cc7b2e..8cde594178 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java @@ -60,16 +60,17 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, private static final String AFTER_ACL_PARENT = "AFTER_ACL_PARENT"; private PermissionService permissionService; - private NamespacePrefixResolver nspr; - private NodeService nodeService; - private AuthenticationService authenticationService; + private int maxPermissionChecks; + private long maxPermissionCheckTimeMillis; public ACLEntryAfterInvocationProvider() { super(); + maxPermissionChecks = Integer.MAX_VALUE; + maxPermissionCheckTimeMillis = Long.MAX_VALUE; } public void setPermissionService(PermissionService permissionService) @@ -111,6 +112,16 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, { this.authenticationService = authenticationService; } + + public void setMaxPermissionChecks(int maxPermissionChecks) + { + this.maxPermissionChecks = maxPermissionChecks; + } + + public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis) + { + this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis; + } public void afterPropertiesSet() throws Exception { @@ -401,10 +412,24 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, } } - + // record the start time + long startTimeMillis = System.currentTimeMillis(); + // set the default, unlimited resultset type + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); for (int i = 0; i < returnedObject.length(); i++) { + long currentTimeMillis = System.currentTimeMillis(); + if ( i >= maxPermissionChecks || (currentTimeMillis - startTimeMillis) > maxPermissionCheckTimeMillis) + { + filteringResultSet.setResultSetMetaData( + new SimpleResultSetMetaData( + LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, + PermissionEvaluationMode.EAGER, + returnedObject.getResultSetMetaData().getSearchParameters())); + break; + } + // All permission checks must pass filteringResultSet.setIncluded(i, true); @@ -429,16 +454,14 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, } // Bug out if we are limiting by size - - if((maxSize != null) && (filteringResultSet.length() > maxSize.intValue())) + if ((maxSize != null) && (filteringResultSet.length() > maxSize.intValue())) { // Renove the last match to fix the correct size filteringResultSet.setIncluded(i, false); filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.FINAL_SIZE, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); - return filteringResultSet; + break; } } - filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); return filteringResultSet; } @@ -465,8 +488,24 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, log.debug("Entries are " + supportedDefinitions); } - for (Object nextObject : returnedObject) + // record search start time + long startTimeMillis = System.currentTimeMillis(); + int count = 0; + + Iterator iterator = returnedObject.iterator(); + while (iterator.hasNext()) { + Object nextObject = iterator.next(); + + // if the maximum result size or time has been exceeded, then we have to remove only + long currentTimeMillis = System.currentTimeMillis(); + if ( count >= maxPermissionChecks || (currentTimeMillis - startTimeMillis) > maxPermissionCheckTimeMillis) + { + // just remove it + iterator.remove(); + continue; + } + boolean allowed = true; for (ConfigAttributeDefintion cad : supportedDefinitions) { diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java index d7c6938916..6fa8f16c31 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java @@ -318,6 +318,11 @@ public class PermissionModel implements ModelDAO, InitializingBean private void addTypePermissions(QName type, Set permissions, boolean exposedOnly) { TypeDefinition typeDef = dictionaryService.getType(type); + if (typeDef == null) + { + // the type definition is no longer in the dictionary - ignore + return; + } if (typeDef.getParentName() != null) { PermissionSet permissionSet = permissionSets.get(type); @@ -342,6 +347,11 @@ public class PermissionModel implements ModelDAO, InitializingBean private void addAspectPermissions(QName type, Set permissions, boolean exposedOnly) { AspectDefinition aspectDef = dictionaryService.getAspect(type); + if (aspectDef == null) + { + // the aspect definition is no longer in the dictionary - ignore + return; + } if (aspectDef.getParentName() != null) { PermissionSet permissionSet = permissionSets.get(type); diff --git a/source/java/org/alfresco/repo/template/NodeSearchResultsMap.java b/source/java/org/alfresco/repo/template/NodeSearchResultsMap.java index 30b642f7e7..69fc35631c 100644 --- a/source/java/org/alfresco/repo/template/NodeSearchResultsMap.java +++ b/source/java/org/alfresco/repo/template/NodeSearchResultsMap.java @@ -18,6 +18,7 @@ package org.alfresco.repo.template; import java.util.List; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.TemplateNode; @@ -47,8 +48,7 @@ public class NodeSearchResultsMap extends BaseSearchResultsMap TemplateNode result = null; if (key != null) { - String ref = key.toString().replace(":", "\\:"); - ref = "ID:" + ref.replace("/", "\\/"); + String ref = "ID:" + LuceneQueryParser.escape(key.toString()); List results = query(ref); diff --git a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java index 9927d1a5ba..a82dd69bab 100644 --- a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java +++ b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java @@ -58,6 +58,7 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest protected TransactionService transactionService; protected MutableAuthenticationDao authenticationDAO; protected NodeArchiveService nodeArchiveService; + protected NodeService nodeService; /* * Data used by tests @@ -139,6 +140,7 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent"); this.authenticationDAO = (MutableAuthenticationDao) applicationContext.getBean("alfDaoImpl"); this.nodeArchiveService = (NodeArchiveService) applicationContext.getBean("nodeArchiveService"); + this.nodeService = (NodeService)applicationContext.getBean("nodeService"); authenticationService.clearCurrentSecurityContext(); diff --git a/source/java/org/alfresco/repo/version/NodeServiceImpl.java b/source/java/org/alfresco/repo/version/NodeServiceImpl.java index 0de161ca00..c7d73b6468 100644 --- a/source/java/org/alfresco/repo/version/NodeServiceImpl.java +++ b/source/java/org/alfresco/repo/version/NodeServiceImpl.java @@ -29,6 +29,7 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.InvalidAspectException; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.AssociationExistsException; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -39,6 +40,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.NodeRef.Status; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QNamePattern; @@ -301,31 +303,29 @@ public class NodeServiceImpl implements NodeService, VersionModel public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException { Map result = new HashMap(); - - // TODO should be doing this using a path query .. - Collection children = this.dbNodeService.getChildAssocs(convertNodeRef(nodeRef)); + Collection children = this.dbNodeService.getChildAssocs(convertNodeRef(nodeRef), CHILD_QNAME_VERSIONED_ATTRIBUTES, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef child : children) { - if (child.getQName().equals(CHILD_QNAME_VERSIONED_ATTRIBUTES)) - { - NodeRef versionedAttribute = child.getChildRef(); + NodeRef versionedAttribute = child.getChildRef(); - // Get the QName and the value - Serializable value = null; - QName qName = (QName)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_QNAME); - Boolean isMultiValue = (Boolean)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_IS_MULTI_VALUE); - if (isMultiValue.booleanValue() == false) - { - value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_VALUE); - } - else - { - value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_MULTI_VALUE); - } - - result.put(qName, value); + // Get the QName and the value + Serializable value = null; + QName qName = (QName)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_QNAME); + PropertyDefinition propDef = this.dicitionaryService.getProperty(qName); + + Boolean isMultiValue = (Boolean)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_IS_MULTI_VALUE); + if (isMultiValue.booleanValue() == false) + { + value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_VALUE); + value = (Serializable)DefaultTypeConverter.INSTANCE.convert(propDef.getDataType(), value); } + else + { + value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_MULTI_VALUE); + } + + result.put(qName, value); } return result; diff --git a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java index e99db11f69..22169e7b40 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java @@ -19,11 +19,13 @@ package org.alfresco.repo.version; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; +import java.util.Map; import java.util.Set; import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -31,6 +33,7 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionServiceException; +import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.namespace.QName; /** @@ -681,4 +684,30 @@ public class VersionServiceImplTest extends BaseVersionStoreTest } }); } + + public void testAR807() + { + QName prop = QName.createQName("http://www.alfresco.org/test/versionstorebasetest/1.0", "intProp"); + + ChildAssociationRef childAssociation = + nodeService.createNode(this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("http://www.alfresco.org/test/versionstorebasetest/1.0", "integerTest"), + TEST_TYPE_QNAME); + NodeRef newNode = childAssociation.getChildRef(); + nodeService.setProperty(newNode, prop, 1); + + Object editionCode = nodeService.getProperty(newNode, prop); + assertEquals(editionCode.getClass(), Integer.class); + + Map versionProps = new HashMap(1); + versionProps.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); + Version version = versionService.createVersion(newNode, versionProps); + + NodeRef versionNodeRef = version.getFrozenStateNodeRef(); + assertNotNull(versionNodeRef); + + Object editionCodeArchive = nodeService.getProperty(versionNodeRef, prop); + assertEquals(editionCodeArchive.getClass(), Integer.class); + } } diff --git a/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml b/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml index 1d36ad3cf7..2d707a64bc 100644 --- a/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml +++ b/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml @@ -41,6 +41,9 @@ d:text true + + d:int + diff --git a/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java b/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java index a1a09460c3..94bad519cb 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java @@ -100,8 +100,6 @@ public class WorkflowInterpreter /** * Main entry point. - * - * Syntax: AVMInteractiveConsole storage (new|old). */ public static void main(String[] args) { @@ -206,6 +204,8 @@ public class WorkflowInterpreter /** * Interpret a single command using the BufferedReader passed in for any data needed. * + * TODO: Use decent parser! + * * @param line The unparsed command * @return The textual output of the command. */ @@ -300,14 +300,41 @@ public class WorkflowInterpreter else if (command[1].equals("workflows")) { - if (currentWorkflowDef == null) + String id = (currentWorkflowDef != null) ? currentWorkflowDef.id : null; + if (id == null && command.length == 2) { - return "workflow definition not in use. Enter command use .\n"; + return "workflow definition not in use. Enter command 'show workflows all' or 'use '.\n"; } - List workflows = workflowService.getActiveWorkflows(currentWorkflowDef.id); - for (WorkflowInstance workflow : workflows) + if (command.length == 3) { - out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title); + if (command[2].equals("all")) + { + id = "all"; + } + else + { + return "Syntax Error.\n"; + } + } + + if (id.equals("all")) + { + for (WorkflowDefinition def : workflowService.getDefinitions()) + { + List workflows = workflowService.getActiveWorkflows(def.id); + for (WorkflowInstance workflow : workflows) + { + out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title); + } + } + } + else + { + List workflows = workflowService.getActiveWorkflows(id); + for (WorkflowInstance workflow : workflows) + { + out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title); + } } } @@ -485,6 +512,25 @@ public class WorkflowInterpreter out.print(interpretCommand("deploy " + currentDeploy)); } + else if (command[0].equals("undeploy")) + { + if (command.length < 2) + { + return "Syntax Error.\n"; + } + if (command[1].equals("definition")) + { + if (command.length != 3) + { + return "Syntax Error.\n"; + } + workflowService.undeployDefinition(command[2]); + currentWorkflowDef = null; + currentPath = null; + out.print(interpretCommand("show definitions")); + } + } + else if (command[0].equals("use")) { if (command.length == 1) @@ -526,8 +572,7 @@ public class WorkflowInterpreter { return "Syntax Error.\n"; } - } - + } } else if (command[0].equals("user")) @@ -659,11 +704,11 @@ public class WorkflowInterpreter else if (command[0].equals("delete")) { - if (command.length < 3) + if (command.length < 2) { return "Syntax Error.\n"; } - else if (command[1].equals("workflow")) + if (command[1].equals("workflow")) { String workflowId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.instance.id; if (workflowId == null) @@ -673,6 +718,40 @@ public class WorkflowInterpreter workflowService.deleteWorkflow(workflowId); out.println("workflow " + workflowId + " deleted."); } + else if (command[1].equals("all")) + { + if (command.length < 3) + { + return "Syntax Error.\n"; + } + if (command[2].equals("workflows")) + { + if (command.length < 4) + { + return "Enter the command 'delete all workflows imeanit' to really delete all workflows\n"; + } + if (command[3].equals("imeanit")) + { + for (WorkflowDefinition def : workflowService.getDefinitions()) + { + List workflows = workflowService.getActiveWorkflows(def.id); + for (WorkflowInstance workflow : workflows) + { + workflowService.deleteWorkflow(workflow.id); + out.println("workflow " + workflow.id + " deleted."); + } + } + } + else + { + return "Syntax Error.\n"; + } + } + else + { + return "Syntax Error.\n"; + } + } else { return "Syntax Error.\n"; diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java index 59f09962af..b7e8f4fb86 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java @@ -21,7 +21,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.alfresco.repo.jscript.Classification; +import org.alfresco.repo.jscript.Search; +import org.alfresco.repo.jscript.Session; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.workflow.WorkflowException; import org.dom4j.Element; @@ -54,6 +58,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null); private ScriptService scriptService; + private ServiceRegistry services; private Element script; @@ -64,6 +69,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler protected void initialiseHandler(BeanFactory factory) { scriptService = (ScriptService)factory.getBean(ServiceRegistry.SCRIPT_SERVICE.getLocalName()); + services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY); } @@ -140,6 +146,16 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler { Map inputMap = new HashMap(); + // initialise global script variables + JBPMNode companyHome = (JBPMNode)executionContext.getContextInstance().getVariable("companyhome"); + if (companyHome != null) + { + NodeRef companyHomeRef = companyHome.getNodeRef(); + inputMap.put("search", new Search(services, companyHomeRef.getStoreRef(), null)); + inputMap.put("session", new Session(services, null)); + inputMap.put("classification", new Classification(services, companyHomeRef.getStoreRef(), null)); + } + // initialise process variables Token token = executionContext.getToken(); inputMap.put("executionContext", executionContext); diff --git a/source/java/org/alfresco/service/cmr/repository/NodeService.java b/source/java/org/alfresco/service/cmr/repository/NodeService.java index 936afe1892..4398cfd981 100644 --- a/source/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/source/java/org/alfresco/service/cmr/repository/NodeService.java @@ -30,6 +30,24 @@ import org.alfresco.service.namespace.QNamePattern; /** * Interface for public and internal node and store operations. + *

+ * Amongst other things, this service must enforce the unique name check as mandated + * by the duplicate entity in the model. + *


+ *    
+ *       ...
+ *       
+ *          
+ *             ...
+ *             false
+ *          
+ *       
+ *    
+ * 
+ * When duplicates are not allowed, and the cm:name property of a node changes, + * then the {@link org.alfresco.service.cmr.repository.DuplicateChildNodeNameException} + * exception must be thrown. Client code can catch this exception and deal with it + * appropriately. * * @author Derek Hulley */ diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptLocation.java b/source/java/org/alfresco/service/cmr/repository/ScriptLocation.java new file mode 100644 index 0000000000..b1bbe8bb4d --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/ScriptLocation.java @@ -0,0 +1,22 @@ +/** + * + */ +package org.alfresco.service.cmr.repository; + +import java.io.Reader; + +/** + * Interface encapsulating the location of a script and provding access to it. + * + * @author Roy Wetherall + * + */ +public interface ScriptLocation +{ + /** + * Returns a reader to the contents of the script + * + * @return the reader + */ + Reader getReader(); +} diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptService.java b/source/java/org/alfresco/service/cmr/repository/ScriptService.java index 80112477f7..0338bfa6cb 100644 --- a/source/java/org/alfresco/service/cmr/repository/ScriptService.java +++ b/source/java/org/alfresco/service/cmr/repository/ScriptService.java @@ -70,6 +70,20 @@ public interface ScriptService public Object executeScript(NodeRef scriptRef, QName contentProp, Map model) throws ScriptException; + /** + * Process a script against the supplied data model. + * + * @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 = {"scriptLocation", "model"}) + public Object executeScript(ScriptLocation scriptLocation, Map model) + throws ScriptException; + /** * Process a script against the supplied data model. * diff --git a/source/java/org/alfresco/service/cmr/search/LimitBy.java b/source/java/org/alfresco/service/cmr/search/LimitBy.java index f275298840..f7831c369b 100644 --- a/source/java/org/alfresco/service/cmr/search/LimitBy.java +++ b/source/java/org/alfresco/service/cmr/search/LimitBy.java @@ -7,5 +7,18 @@ package org.alfresco.service.cmr.search; */ public enum LimitBy { - UNLIMITED, FINAL_SIZE; // NUMBER_OF_PERMISSION_EVALUATIONS + /** + * The final number of search results is not important. + */ + UNLIMITED, + /** + * Limit the total number of search results returned after pruning by permissions. + */ + FINAL_SIZE, + /** + * Limit the number of results that will be passed through for permission checks.
+ * Used internally to prevent excessive permission checking + * (see property lucene.query.maxInitialSearchResults). + */ + NUMBER_OF_PERMISSION_EVALUATIONS; } \ No newline at end of file