mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Moving to root below branch label
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
28
source/java/org/alfresco/repo/policy/AssociationPolicy.java
Normal file
28
source/java/org/alfresco/repo/policy/AssociationPolicy.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* Marker interface for representing an Association-level Policy.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public interface AssociationPolicy extends Policy
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Delegate for a Class Feature-level (Property and Association) Policies. Provides
|
||||
* access to Policy Interface implementations which invoke the appropriate bound behaviours.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
*/
|
||||
public class AssociationPolicyDelegate<P extends AssociationPolicy>
|
||||
{
|
||||
private DictionaryService dictionary;
|
||||
private CachedPolicyFactory<ClassFeatureBehaviourBinding, P> factory;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param policyClass the policy interface class
|
||||
* @param index the behaviour index to query against
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
/*package*/ AssociationPolicyDelegate(DictionaryService dictionary, Class<P> policyClass, BehaviourIndex<ClassFeatureBehaviourBinding> index)
|
||||
{
|
||||
// Get list of all pre-registered behaviours for the policy and
|
||||
// ensure they are valid.
|
||||
Collection<BehaviourDefinition> definitions = index.getAll();
|
||||
for (BehaviourDefinition definition : definitions)
|
||||
{
|
||||
definition.getBehaviour().getInterface(policyClass);
|
||||
}
|
||||
|
||||
// Rely on cached implementation of policy factory
|
||||
// Note: Could also use PolicyFactory (without caching)
|
||||
this.factory = new CachedPolicyFactory<ClassFeatureBehaviourBinding, P>(policyClass, index);
|
||||
this.dictionary = dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the validity of the given assoc type
|
||||
*
|
||||
* @param assocTypeQName
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void checkAssocType(QName assocTypeQName) throws IllegalArgumentException
|
||||
{
|
||||
AssociationDefinition assocDef = dictionary.getAssociation(assocTypeQName);
|
||||
if (assocDef == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Association " + assocTypeQName + " has not been defined in the data dictionary");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Policy implementation for the specified Class and Association
|
||||
*
|
||||
* When multiple behaviours are bound to the policy for the class feature, an
|
||||
* aggregate policy implementation is returned which invokes each policy
|
||||
* in turn.
|
||||
*
|
||||
* @param classQName the class qualified name
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return the policy
|
||||
*/
|
||||
public P get(QName classQName, QName assocTypeQName)
|
||||
{
|
||||
return get(null, classQName, assocTypeQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Policy implementation for the specified Class and Association
|
||||
*
|
||||
* When multiple behaviours are bound to the policy for the class feature, an
|
||||
* aggregate policy implementation is returned which invokes each policy
|
||||
* in turn.
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the class qualified name
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return the policy
|
||||
*/
|
||||
public P get(NodeRef nodeRef, QName classQName, QName assocTypeQName)
|
||||
{
|
||||
checkAssocType(assocTypeQName);
|
||||
return factory.create(new ClassFeatureBehaviourBinding(dictionary, nodeRef, classQName, assocTypeQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of Policy implementations for the specified Class and Association
|
||||
*
|
||||
* @param classQName the class qualified name
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(QName classQName, QName assocTypeQName)
|
||||
{
|
||||
return getList(null, classQName, assocTypeQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of Policy implementations for the specified Class and Association
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the class qualified name
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(NodeRef nodeRef, QName classQName, QName assocTypeQName)
|
||||
{
|
||||
checkAssocType(assocTypeQName);
|
||||
return factory.createList(new ClassFeatureBehaviourBinding(dictionary, nodeRef, classQName, assocTypeQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <tt>Policy</tt> for all the given Class and Association
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return Return the policy
|
||||
*/
|
||||
public P get(Set<QName> classQNames, QName assocTypeQName)
|
||||
{
|
||||
return get(null, classQNames, assocTypeQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <tt>Policy</tt> for all the given Class and Association
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQNames the class qualified names
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return Return the policy
|
||||
*/
|
||||
public P get(NodeRef nodeRef, Set<QName> classQNames, QName assocTypeQName)
|
||||
{
|
||||
checkAssocType(assocTypeQName);
|
||||
return factory.toPolicy(getList(nodeRef, classQNames, assocTypeQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the <tt>Policy</tt> instances for all the given Classes and Associations
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return Return the policies
|
||||
*/
|
||||
public Collection<P> getList(Set<QName> classQNames, QName assocTypeQName)
|
||||
{
|
||||
return getList(null, classQNames, assocTypeQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the <tt>Policy</tt> instances for all the given Classes and Associations
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQNames the class qualified names
|
||||
* @param assocTypeQName the association type qualified name
|
||||
* @return Return the policies
|
||||
*/
|
||||
public Collection<P> getList(NodeRef nodeRef, Set<QName> classQNames, QName assocTypeQName)
|
||||
{
|
||||
checkAssocType(assocTypeQName);
|
||||
Collection<P> policies = new HashSet<P>();
|
||||
for (QName classQName : classQNames)
|
||||
{
|
||||
P policy = factory.create(new ClassFeatureBehaviourBinding(dictionary, nodeRef, classQName, assocTypeQName));
|
||||
if (policy instanceof PolicyList)
|
||||
{
|
||||
policies.addAll(((PolicyList<P>)policy).getPolicies());
|
||||
}
|
||||
else
|
||||
{
|
||||
policies.add(policy);
|
||||
}
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
|
||||
}
|
55
source/java/org/alfresco/repo/policy/Behaviour.java
Normal file
55
source/java/org/alfresco/repo/policy/Behaviour.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* A Behaviour represents an encapsulated piece of logic (system or business)
|
||||
* that may be bound to a Policy. The logic may be expressed in any
|
||||
* language (java, script etc).
|
||||
*
|
||||
* Once bound to a Policy, the behaviour must be able to provide the interface
|
||||
* declared by that policy.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public interface Behaviour
|
||||
{
|
||||
/**
|
||||
* Gets the requested policy interface onto the behaviour
|
||||
*
|
||||
* @param policy the policy interface class
|
||||
* @return the policy interface
|
||||
*/
|
||||
public <T> T getInterface(Class<T> policy);
|
||||
|
||||
/**
|
||||
* Disable the behaviour (for this thread only)
|
||||
*/
|
||||
public void disable();
|
||||
|
||||
/**
|
||||
* Enable the behaviour (for this thread only)
|
||||
*/
|
||||
public void enable();
|
||||
|
||||
/**
|
||||
* @return is the behaviour enabled (for this thread only)
|
||||
*/
|
||||
public boolean isEnabled();
|
||||
|
||||
}
|
38
source/java/org/alfresco/repo/policy/BehaviourBinding.java
Normal file
38
source/java/org/alfresco/repo/policy/BehaviourBinding.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* A Behaviour Binding represents the way in which a Behaviour is bound
|
||||
* to a Policy i.e. the key.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
/*package*/ interface BehaviourBinding
|
||||
{
|
||||
/**
|
||||
* Gets a generalised form of the Binding.
|
||||
*
|
||||
* For example, if the binding key is hierarchical, return the parent
|
||||
* key.
|
||||
*
|
||||
* @return the generalised form (or null, if there isn't one)
|
||||
*/
|
||||
BehaviourBinding generaliseBinding();
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* An Observer interface for listening to changes in behaviour bindings.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> The specific type of Behaviour Binding to listen out for.
|
||||
*/
|
||||
/*package*/ interface BehaviourChangeObserver<B extends BehaviourBinding>
|
||||
{
|
||||
/**
|
||||
* A new binding has been made.
|
||||
*
|
||||
* @param binding the binding
|
||||
* @param behaviour the behaviour attached to the binding
|
||||
*/
|
||||
public void addition(B binding, Behaviour behaviour);
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Description of a bound Behaviour.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> The type of Binding.
|
||||
*/
|
||||
public interface BehaviourDefinition<B extends BehaviourBinding>
|
||||
{
|
||||
/**
|
||||
* Gets the Policy bound to
|
||||
*
|
||||
* @return the policy name
|
||||
*/
|
||||
public QName getPolicy();
|
||||
|
||||
/**
|
||||
* Gets the definition of the Policy bound to
|
||||
*
|
||||
* @return the policy definition (or null, if the Policy has not been registered yet)
|
||||
*/
|
||||
public PolicyDefinition getPolicyDefinition();
|
||||
|
||||
/**
|
||||
* Gets the binding used to bind the Behaviour to the Policy
|
||||
*
|
||||
* @return the binding
|
||||
*/
|
||||
public B getBinding();
|
||||
|
||||
/**
|
||||
* Gets the Behaviour
|
||||
*
|
||||
* @return the behaviour
|
||||
*/
|
||||
public Behaviour getBehaviour();
|
||||
}
|
100
source/java/org/alfresco/repo/policy/BehaviourFilter.java
Normal file
100
source/java/org/alfresco/repo/policy/BehaviourFilter.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Contract disabling and enabling policy behaviours.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public interface BehaviourFilter
|
||||
{
|
||||
/**
|
||||
* Disable behaviour for all nodes
|
||||
*
|
||||
* @param className the type/aspect behaviour to disable
|
||||
* @return true => already disabled
|
||||
*/
|
||||
public boolean disableBehaviour(QName className);
|
||||
|
||||
/**
|
||||
* Disable behaviour for specific node
|
||||
*
|
||||
* @param nodeRef the node to disable for
|
||||
* @param className the type/aspect behaviour to disable
|
||||
* @return true => already disabled
|
||||
*/
|
||||
public boolean disableBehaviour(NodeRef nodeRef, QName className);
|
||||
|
||||
/**
|
||||
* Enable behaviour for all nodes
|
||||
*
|
||||
* @param className the type/aspect behaviour to enable
|
||||
*/
|
||||
public void enableBehaviour(QName className);
|
||||
|
||||
/**
|
||||
* Enable behaviour for specific node
|
||||
*
|
||||
* @param nodeRef the node to enable for
|
||||
* @param className the type/aspect behaviour to enable
|
||||
*/
|
||||
public void enableBehaviour(NodeRef nodeRef, QName className);
|
||||
|
||||
/**
|
||||
* Enable all behaviours for specific node
|
||||
*
|
||||
* @param nodeRef the node to enable for
|
||||
*/
|
||||
public void enableBehaviours(NodeRef nodeRef);
|
||||
|
||||
/**
|
||||
* Enable all behaviours
|
||||
*/
|
||||
public void enableAllBehaviours();
|
||||
|
||||
/**
|
||||
* Determine if behaviour is enabled across all nodes.
|
||||
*
|
||||
* @param className the behaviour to test for
|
||||
* @return true => behaviour is enabled
|
||||
*/
|
||||
public boolean isEnabled(QName className);
|
||||
|
||||
/**
|
||||
* Determine if behaviour is enabled for specific node.
|
||||
*
|
||||
* Note: A node behaviour is enabled only when:
|
||||
* a) the behaviour is not disabled across all nodes
|
||||
* b) the behaviour is not disabled specifically for the provided node
|
||||
*
|
||||
* @param nodeRef the node to test for
|
||||
* @param className the behaviour to test for
|
||||
* @return true => behaviour is enabled
|
||||
*/
|
||||
public boolean isEnabled(NodeRef nodeRef, QName className);
|
||||
|
||||
/**
|
||||
* Determine if any behaviours have been disabled?
|
||||
*
|
||||
* @return true => behaviours have been filtered
|
||||
*/
|
||||
public boolean isActivated();
|
||||
}
|
224
source/java/org/alfresco/repo/policy/BehaviourFilterImpl.java
Normal file
224
source/java/org/alfresco/repo/policy/BehaviourFilterImpl.java
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Implementation of Behaviour Filter.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public class BehaviourFilterImpl implements BehaviourFilter
|
||||
{
|
||||
// Thread local storage of filters
|
||||
ThreadLocal<List<QName>> classFilter = new ThreadLocal<List<QName>>();
|
||||
ThreadLocal<Map<NodeRef,List<QName>>> nodeRefFilter = new ThreadLocal<Map<NodeRef,List<QName>>>();
|
||||
|
||||
// Dictionary Service
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
/**
|
||||
* @param dictionaryService dictionary service
|
||||
*/
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#disableBehaviour(org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean disableBehaviour(QName className)
|
||||
{
|
||||
List<QName> classNames = classFilter.get();
|
||||
if (classNames == null)
|
||||
{
|
||||
classNames = new ArrayList<QName>();
|
||||
classFilter.set(classNames);
|
||||
}
|
||||
boolean alreadyDisabled = classNames.contains(className);
|
||||
if (!alreadyDisabled)
|
||||
{
|
||||
classNames.add(className);
|
||||
}
|
||||
return alreadyDisabled;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#disableBehaviour(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean disableBehaviour(NodeRef nodeRef, QName className)
|
||||
{
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters == null)
|
||||
{
|
||||
filters = new HashMap<NodeRef,List<QName>>();
|
||||
nodeRefFilter.set(filters);
|
||||
}
|
||||
List<QName> classNames = filters.get(nodeRef);
|
||||
if (classNames == null)
|
||||
{
|
||||
classNames = new ArrayList<QName>();
|
||||
filters.put(nodeRef, classNames);
|
||||
}
|
||||
boolean alreadyDisabled = classNames.contains(className);
|
||||
if (!alreadyDisabled)
|
||||
{
|
||||
classNames.add(className);
|
||||
}
|
||||
return alreadyDisabled;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableBehaviour(org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public void enableBehaviour(QName className)
|
||||
{
|
||||
List<QName> classNames = classFilter.get();
|
||||
if (classNames != null)
|
||||
{
|
||||
classNames.remove(className);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableBehaviour(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public void enableBehaviour(NodeRef nodeRef, QName className)
|
||||
{
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters != null)
|
||||
{
|
||||
List<QName> classNames = filters.get(nodeRef);
|
||||
if (classNames != null)
|
||||
{
|
||||
classNames.remove(className);
|
||||
}
|
||||
if (classNames.size() == 0)
|
||||
{
|
||||
filters.remove(nodeRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableBehaviours(org.alfresco.service.cmr.repository.NodeRef)
|
||||
*/
|
||||
public void enableBehaviours(NodeRef nodeRef)
|
||||
{
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters != null)
|
||||
{
|
||||
filters.remove(nodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableAllBehaviours()
|
||||
*/
|
||||
public void enableAllBehaviours()
|
||||
{
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters != null)
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#isEnabled(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean isEnabled(NodeRef nodeRef, QName className)
|
||||
{
|
||||
// check global filters
|
||||
if (!isEnabled(className))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check node level filters
|
||||
Map<NodeRef,List<QName>> nodeFilters = nodeRefFilter.get();
|
||||
if (nodeFilters != null)
|
||||
{
|
||||
List<QName> nodeClassFilters = nodeFilters.get(nodeRef);
|
||||
if (nodeClassFilters != null)
|
||||
{
|
||||
boolean filtered = nodeClassFilters.contains(className);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (QName filterName : nodeClassFilters)
|
||||
{
|
||||
filtered = dictionaryService.isSubClass(className, filterName);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#isEnabled(org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean isEnabled(QName className)
|
||||
{
|
||||
// check global class filters
|
||||
List<QName> classFilters = classFilter.get();
|
||||
if (classFilters != null)
|
||||
{
|
||||
boolean filtered = classFilters.contains(className);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (QName filterName : classFilters)
|
||||
{
|
||||
filtered = dictionaryService.isSubClass(className, filterName);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#isActivated()
|
||||
*/
|
||||
public boolean isActivated()
|
||||
{
|
||||
List<QName> classFilters = classFilter.get();
|
||||
Map<NodeRef,List<QName>> nodeFilters = nodeRefFilter.get();
|
||||
return (classFilters != null && !classFilters.isEmpty()) || (nodeFilters != null && !nodeFilters.isEmpty());
|
||||
}
|
||||
|
||||
}
|
62
source/java/org/alfresco/repo/policy/BehaviourIndex.java
Normal file
62
source/java/org/alfresco/repo/policy/BehaviourIndex.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Index of Bound Behaviours.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> the type of Binding.
|
||||
*/
|
||||
/*package*/ interface BehaviourIndex<B extends BehaviourBinding>
|
||||
{
|
||||
/**
|
||||
* Gets all bound behaviours
|
||||
*
|
||||
* @return the bound behaviours
|
||||
*/
|
||||
public Collection<BehaviourDefinition> getAll();
|
||||
|
||||
/**
|
||||
* Gets all bound behaviours for the specified binding.
|
||||
*
|
||||
* Note: The index may use any algorithm for determining which behaviours
|
||||
* are returned for the binding e.g. based on hierarchical binding
|
||||
*
|
||||
* @param binding the binding
|
||||
* @return the associated behaviours
|
||||
*/
|
||||
public Collection<BehaviourDefinition> find(B binding);
|
||||
|
||||
/**
|
||||
* Add a Behaviour Change Observer.
|
||||
*
|
||||
* @param observer the observer
|
||||
*/
|
||||
public void addChangeObserver(BehaviourChangeObserver<B> observer);
|
||||
|
||||
/**
|
||||
* Gets the behaviour filter
|
||||
*
|
||||
* @return the behaviour filter
|
||||
*/
|
||||
public BehaviourFilter getFilter();
|
||||
}
|
106
source/java/org/alfresco/repo/policy/BehaviourMap.java
Normal file
106
source/java/org/alfresco/repo/policy/BehaviourMap.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Simple Map of Binding to Behaviour with observer support.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> the type of binding.
|
||||
*/
|
||||
/*package*/ class BehaviourMap<B extends BehaviourBinding>
|
||||
{
|
||||
/**
|
||||
* The map of bindings to behaviour
|
||||
*/
|
||||
private Map<B, BehaviourDefinition<B>> index = new HashMap<B, BehaviourDefinition<B>>();
|
||||
|
||||
/**
|
||||
* The list of registered observers
|
||||
*/
|
||||
private List<BehaviourChangeObserver<B>> observers = new ArrayList<BehaviourChangeObserver<B>>();
|
||||
|
||||
|
||||
/**
|
||||
* Binds a Behaviour into the Map
|
||||
*
|
||||
* @param behaviourDefinition the behaviour definition to bind
|
||||
*/
|
||||
public void put(BehaviourDefinition<B> behaviourDefinition)
|
||||
{
|
||||
B binding = behaviourDefinition.getBinding();
|
||||
index.put(binding, behaviourDefinition);
|
||||
for (BehaviourChangeObserver<B> listener : observers)
|
||||
{
|
||||
listener.addition(binding, behaviourDefinition.getBehaviour());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a Behaviour from the Map
|
||||
*
|
||||
* @param binding the binding
|
||||
* @return the behaviour
|
||||
*/
|
||||
public BehaviourDefinition<B> get(B binding)
|
||||
{
|
||||
return index.get(binding);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets all bound Behaviours from the Map
|
||||
*
|
||||
* @return all bound behaviours
|
||||
*/
|
||||
public Collection<BehaviourDefinition<B>> getAll()
|
||||
{
|
||||
return index.values();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the count of bound behaviours
|
||||
*
|
||||
* @return the count
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return index.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a Change Observer
|
||||
*
|
||||
* @param observer the change observer
|
||||
*/
|
||||
public void addChangeObserver(BehaviourChangeObserver<B> observer)
|
||||
{
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
}
|
251
source/java/org/alfresco/repo/policy/CachedPolicyFactory.java
Normal file
251
source/java/org/alfresco/repo/policy/CachedPolicyFactory.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Policy Factory with caching support.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> the type of Binding
|
||||
* @param <P> the type of Policy
|
||||
*/
|
||||
/*package*/ class CachedPolicyFactory<B extends BehaviourBinding, P extends Policy> extends PolicyFactory<B, P>
|
||||
{
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(PolicyComponentImpl.class);
|
||||
|
||||
// Behaviour Filter
|
||||
private BehaviourFilter behaviourFilter = null;
|
||||
|
||||
// Cache Lock
|
||||
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
/**
|
||||
* Cache for a single Policy interface (keyed by Binding)
|
||||
*/
|
||||
private Map<B, P> singleCache = new HashMap<B, P>();
|
||||
|
||||
/**
|
||||
* Cache for a collection of Policy interfaces (keyed by Binding)
|
||||
*/
|
||||
private Map<B, Collection<P>> listCache = new HashMap<B, Collection<P>>();
|
||||
|
||||
|
||||
/**
|
||||
* Construct cached policy factory
|
||||
*
|
||||
* @param policyClass the policy interface class
|
||||
* @param index the behaviour index to search on
|
||||
*/
|
||||
/*package*/ CachedPolicyFactory(Class<P> policyClass, BehaviourIndex<B> index)
|
||||
{
|
||||
super(policyClass, index);
|
||||
behaviourFilter = index.getFilter();
|
||||
|
||||
// Register this cached policy factory as a change observer of the behaviour index
|
||||
// to allow for cache to be cleared appropriately.
|
||||
index.addChangeObserver(new BehaviourChangeObserver<B>()
|
||||
{
|
||||
public void addition(B binding, Behaviour behaviour)
|
||||
{
|
||||
clearCache("aggregate delegate", singleCache, binding);
|
||||
clearCache("delegate collection", listCache, binding);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public P create(B binding)
|
||||
{
|
||||
// When behaviour filters are activated bypass the cache
|
||||
if (behaviourFilter != null && behaviourFilter.isActivated())
|
||||
{
|
||||
return super.create(binding);
|
||||
}
|
||||
|
||||
lock.readLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
P policyInterface = singleCache.get(binding);
|
||||
if (policyInterface == null)
|
||||
{
|
||||
// Upgrade read lock to write lock
|
||||
lock.readLock().unlock();
|
||||
lock.writeLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
// Check again
|
||||
policyInterface = singleCache.get(binding);
|
||||
if (policyInterface == null)
|
||||
{
|
||||
policyInterface = super.create(binding);
|
||||
singleCache.put(binding, policyInterface);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Cached delegate interface " + policyInterface + " for " + binding + " and policy " + getPolicyClass());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Downgrade lock to read
|
||||
lock.readLock().lock();
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
return policyInterface;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<P> createList(B binding)
|
||||
{
|
||||
// When behaviour filters are activated bypass the cache
|
||||
if (behaviourFilter != null && behaviourFilter.isActivated())
|
||||
{
|
||||
return super.createList(binding);
|
||||
}
|
||||
|
||||
lock.readLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
Collection<P> policyInterfaces = listCache.get(binding);
|
||||
if (policyInterfaces == null)
|
||||
{
|
||||
// Upgrade read lock to write lock
|
||||
lock.readLock().unlock();
|
||||
lock.writeLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
// Check again
|
||||
policyInterfaces = listCache.get(binding);
|
||||
if (policyInterfaces == null)
|
||||
{
|
||||
policyInterfaces = super.createList(binding);
|
||||
listCache.put(binding, policyInterfaces);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Cached delegate interface collection " + policyInterfaces + " for " + binding + " and policy " + getPolicyClass());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Downgrade lock to read
|
||||
lock.readLock().lock();
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
return policyInterfaces;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear entries in the cache based on binding changes.
|
||||
*
|
||||
* @param cacheDescription description of cache to clear
|
||||
* @param cache the cache to clear
|
||||
* @param binding the binding
|
||||
*/
|
||||
private void clearCache(String cacheDescription, Map<B, ?> cache, B binding)
|
||||
{
|
||||
if (binding == null)
|
||||
{
|
||||
lock.writeLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
// A specific binding has not been provided, so clear all entries
|
||||
cache.clear();
|
||||
|
||||
if (logger.isDebugEnabled() && cache.isEmpty() == false)
|
||||
logger.debug("Cleared " + cacheDescription + " cache (all class bindings) for policy " + getPolicyClass());
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A specific binding has been provided. Build a list of entries
|
||||
// that require removal. An entry is removed if the binding in the
|
||||
// list is equal or derived from the changed binding.
|
||||
Collection<B> invalidBindings = new ArrayList<B>();
|
||||
for (B cachedBinding : cache.keySet())
|
||||
{
|
||||
// Determine if binding is equal or derived from changed binding
|
||||
BehaviourBinding generalisedBinding = cachedBinding;
|
||||
while(generalisedBinding != null)
|
||||
{
|
||||
if (generalisedBinding.equals(binding))
|
||||
{
|
||||
invalidBindings.add(cachedBinding);
|
||||
break;
|
||||
}
|
||||
generalisedBinding = generalisedBinding.generaliseBinding();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all invalid bindings
|
||||
if (invalidBindings.size() > 0)
|
||||
{
|
||||
lock.writeLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
for (B invalidBinding : invalidBindings)
|
||||
{
|
||||
cache.remove(invalidBinding);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Cleared " + cacheDescription + " cache for " + invalidBinding + " and policy " + getPolicyClass());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
135
source/java/org/alfresco/repo/policy/ClassBehaviourBinding.java
Normal file
135
source/java/org/alfresco/repo/policy/ClassBehaviourBinding.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Behaviour binding to a Class (Type or Aspect) in the Content Model.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
/*package*/ class ClassBehaviourBinding implements BehaviourBinding
|
||||
{
|
||||
// The dictionary service
|
||||
private DictionaryService dictionary;
|
||||
|
||||
// The class qualified name
|
||||
private QName classQName;
|
||||
|
||||
// Instance level node reference
|
||||
private NodeRef nodeRef;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param nodeRef the instance level node reference
|
||||
* @param classQName the Class qualified name
|
||||
*/
|
||||
/*package*/ ClassBehaviourBinding(DictionaryService dictionary, NodeRef nodeRef, QName classQName)
|
||||
{
|
||||
this.dictionary = dictionary;
|
||||
this.nodeRef = nodeRef;
|
||||
this.classQName = classQName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param classQName the Class qualified name
|
||||
*/
|
||||
/*package*/ ClassBehaviourBinding(DictionaryService dictionary, QName classQName)
|
||||
{
|
||||
this(dictionary, null, classQName);
|
||||
}
|
||||
|
||||
/*package*/ DictionaryService getDictionary()
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourBinding#generaliseBinding()
|
||||
*/
|
||||
public BehaviourBinding generaliseBinding()
|
||||
{
|
||||
BehaviourBinding generalisedBinding = null;
|
||||
ClassDefinition classDefinition = dictionary.getClass(classQName);
|
||||
if (classDefinition == null)
|
||||
{
|
||||
throw new PolicyException("Class definition " + classDefinition.getName() + " does not exist.");
|
||||
}
|
||||
|
||||
QName parentClassName = classDefinition.getParentName();
|
||||
if (parentClassName != null)
|
||||
{
|
||||
generalisedBinding = new ClassBehaviourBinding(dictionary, parentClassName);
|
||||
}
|
||||
return generalisedBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instance level node reference
|
||||
*
|
||||
* @return the node reference
|
||||
*/
|
||||
public NodeRef getNodeRef()
|
||||
{
|
||||
return nodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class qualified name
|
||||
*
|
||||
* @return the class qualified name
|
||||
*/
|
||||
public QName getClassQName()
|
||||
{
|
||||
return classQName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null || !(obj instanceof ClassBehaviourBinding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return classQName.equals(((ClassBehaviourBinding)obj).classQName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return classQName.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ClassBinding[class=" + classQName + "]";
|
||||
}
|
||||
|
||||
}
|
216
source/java/org/alfresco/repo/policy/ClassBehaviourIndex.java
Normal file
216
source/java/org/alfresco/repo/policy/ClassBehaviourIndex.java
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Class (Type/Aspect) oriented index of bound behaviours
|
||||
*
|
||||
* Note: Uses Class hierarchy to derive bindings.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
/*package*/ class ClassBehaviourIndex<B extends ClassBehaviourBinding> implements BehaviourIndex<B>
|
||||
{
|
||||
// Lock
|
||||
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
// Map of class bindings
|
||||
private BehaviourMap<B> classMap = new BehaviourMap<B>();
|
||||
|
||||
// Map of service bindings
|
||||
private BehaviourMap<ServiceBehaviourBinding> serviceMap = new BehaviourMap<ServiceBehaviourBinding>();
|
||||
|
||||
// List of registered observers
|
||||
private List<BehaviourChangeObserver<B>> observers = new ArrayList<BehaviourChangeObserver<B>>();
|
||||
|
||||
// Behaviour Filter
|
||||
private BehaviourFilter filter = null;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*/
|
||||
/*package*/ ClassBehaviourIndex(BehaviourFilter filter)
|
||||
{
|
||||
// Observe class binding changes and propagate to our own observers
|
||||
this.classMap.addChangeObserver(new BehaviourChangeObserver<B>()
|
||||
{
|
||||
public void addition(B binding, Behaviour behaviour)
|
||||
{
|
||||
for (BehaviourChangeObserver<B> listener : observers)
|
||||
{
|
||||
listener.addition(binding, behaviour);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Observe service binding changes and propagate to our own observers
|
||||
this.serviceMap.addChangeObserver(new BehaviourChangeObserver<ServiceBehaviourBinding>()
|
||||
{
|
||||
public void addition(ServiceBehaviourBinding binding, Behaviour behaviour)
|
||||
{
|
||||
for (BehaviourChangeObserver<B> listener : observers)
|
||||
{
|
||||
// Note: Don't specify class ref as service-level bindings affect all classes
|
||||
listener.addition(null, behaviour);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Setup state
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourIndex#getAll()
|
||||
*/
|
||||
public Collection<BehaviourDefinition> getAll()
|
||||
{
|
||||
lock.readLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
List<BehaviourDefinition> all = new ArrayList<BehaviourDefinition>(classMap.size() + serviceMap.size());
|
||||
all.addAll(classMap.getAll());
|
||||
all.addAll(serviceMap.getAll());
|
||||
return all;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourIndex#find()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<BehaviourDefinition> find(B binding)
|
||||
{
|
||||
lock.readLock().lock();
|
||||
|
||||
try
|
||||
{
|
||||
List<BehaviourDefinition> behaviours = new ArrayList<BehaviourDefinition>();
|
||||
|
||||
// Determine if behaviour has been disabled
|
||||
boolean isEnabled = true;
|
||||
if (filter != null)
|
||||
{
|
||||
NodeRef nodeRef = binding.getNodeRef();
|
||||
QName className = binding.getClassQName();
|
||||
isEnabled = (nodeRef == null) ? filter.isEnabled(className) : filter.isEnabled(nodeRef, className);
|
||||
}
|
||||
|
||||
if (isEnabled)
|
||||
{
|
||||
// Find class behaviour by scanning up the class hierarchy
|
||||
BehaviourDefinition behaviour = null;
|
||||
while(behaviour == null && binding != null)
|
||||
{
|
||||
behaviour = classMap.get(binding);
|
||||
if (behaviour == null)
|
||||
{
|
||||
binding = (B)binding.generaliseBinding();
|
||||
}
|
||||
}
|
||||
if (behaviour != null)
|
||||
{
|
||||
behaviours.add(behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
// Append all service-level behaviours
|
||||
behaviours.addAll(serviceMap.getAll());
|
||||
|
||||
return behaviours;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourIndex#find()
|
||||
*/
|
||||
public void addChangeObserver(BehaviourChangeObserver<B> observer)
|
||||
{
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourIndex#getFilter()
|
||||
*/
|
||||
public BehaviourFilter getFilter()
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds a Class Behaviour into this index
|
||||
*
|
||||
* @param behaviour the class bound behaviour
|
||||
*/
|
||||
public void putClassBehaviour(BehaviourDefinition<B> behaviour)
|
||||
{
|
||||
lock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
classMap.put(behaviour);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds a Service Behaviour into this index
|
||||
*
|
||||
* @param behaviour the service bound behaviour
|
||||
*/
|
||||
public void putServiceBehaviour(BehaviourDefinition<ServiceBehaviourBinding> behaviour)
|
||||
{
|
||||
lock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
serviceMap.put(behaviour);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Behaviour binding to a Class (Type or Aspect) in the Content Model.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
/*package*/ class ClassFeatureBehaviourBinding extends ClassBehaviourBinding
|
||||
{
|
||||
// The feature qualified name (property or association)
|
||||
private QName featureQName;
|
||||
private QName activeFeatureQName;
|
||||
|
||||
// Wild Card feature match (match all features)
|
||||
private static final QName ALL_FEATURES = QName.createQName("", "*");
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the Class qualified name
|
||||
* @param featureQName the Class feature (property or association) qualifed name
|
||||
*/
|
||||
/*package*/ ClassFeatureBehaviourBinding(DictionaryService dictionary, NodeRef nodeRef, QName classQName, QName featureQName)
|
||||
{
|
||||
this(dictionary, nodeRef, classQName, featureQName, featureQName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param classQName the Class qualified name
|
||||
* @param featureQName the Class feature (property or association) qualifed name
|
||||
*/
|
||||
/*package*/ ClassFeatureBehaviourBinding(DictionaryService dictionary, QName classQName, QName featureQName)
|
||||
{
|
||||
this(dictionary, null, classQName, featureQName, featureQName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the Class qualified name
|
||||
*/
|
||||
/*package*/ ClassFeatureBehaviourBinding(DictionaryService dictionary, NodeRef nodeRef, QName classQName)
|
||||
{
|
||||
this(dictionary, nodeRef, classQName, ALL_FEATURES);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param classQName the Class qualified name
|
||||
*/
|
||||
/*package*/ ClassFeatureBehaviourBinding(DictionaryService dictionary, QName classQName)
|
||||
{
|
||||
this(dictionary, null, classQName, ALL_FEATURES);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the Class qualified name
|
||||
* @param featureQName the Class feature (property or association) qualifed name
|
||||
* @param activeFeatureQName the currently active feature QName
|
||||
*/
|
||||
private ClassFeatureBehaviourBinding(DictionaryService dictionary, NodeRef nodeRef, QName classQName, QName featureQName, QName activeFeatureQName)
|
||||
{
|
||||
super(dictionary, nodeRef, classQName);
|
||||
this.featureQName = featureQName;
|
||||
this.activeFeatureQName = activeFeatureQName;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourBinding#generaliseBinding()
|
||||
*/
|
||||
public BehaviourBinding generaliseBinding()
|
||||
{
|
||||
BehaviourBinding generalisedBinding = null;
|
||||
ClassDefinition classDefinition = getDictionary().getClass(getClassQName());
|
||||
|
||||
if (activeFeatureQName.equals(ALL_FEATURES))
|
||||
{
|
||||
QName parentClassName = classDefinition.getParentName();
|
||||
if (parentClassName != null)
|
||||
{
|
||||
generalisedBinding = new ClassFeatureBehaviourBinding(getDictionary(), getNodeRef(), parentClassName, featureQName, featureQName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
generalisedBinding = new ClassFeatureBehaviourBinding(getDictionary(), getNodeRef(), getClassQName(), featureQName, ALL_FEATURES);
|
||||
}
|
||||
|
||||
return generalisedBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null || !(obj instanceof ClassFeatureBehaviourBinding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return getClassQName().equals(((ClassFeatureBehaviourBinding)obj).getClassQName()) &&
|
||||
activeFeatureQName.equals(((ClassFeatureBehaviourBinding)obj).activeFeatureQName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return 37 * getClassQName().hashCode() + activeFeatureQName.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ClassFeatureBinding[class=" + getClassQName() + ";feature=" + activeFeatureQName + "]";
|
||||
}
|
||||
|
||||
}
|
27
source/java/org/alfresco/repo/policy/ClassPolicy.java
Normal file
27
source/java/org/alfresco/repo/policy/ClassPolicy.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
/**
|
||||
* Marker interface for representing an Class-level Policy.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public interface ClassPolicy extends Policy
|
||||
{
|
||||
}
|
186
source/java/org/alfresco/repo/policy/ClassPolicyDelegate.java
Normal file
186
source/java/org/alfresco/repo/policy/ClassPolicyDelegate.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Delegate for a Class-level Policy. Provides access to Policy Interface
|
||||
* implementations which invoke the appropriate bound behaviours.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
*/
|
||||
public class ClassPolicyDelegate<P extends ClassPolicy>
|
||||
{
|
||||
private DictionaryService dictionary;
|
||||
private CachedPolicyFactory<ClassBehaviourBinding, P> factory;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param policyClass the policy interface class
|
||||
* @param index the behaviour index to query against
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
/*package*/ ClassPolicyDelegate(DictionaryService dictionary, Class<P> policyClass, BehaviourIndex<ClassBehaviourBinding> index)
|
||||
{
|
||||
// Get list of all pre-registered behaviours for the policy and
|
||||
// ensure they are valid.
|
||||
Collection<BehaviourDefinition> definitions = index.getAll();
|
||||
for (BehaviourDefinition definition : definitions)
|
||||
{
|
||||
definition.getBehaviour().getInterface(policyClass);
|
||||
}
|
||||
|
||||
// Rely on cached implementation of policy factory
|
||||
// Note: Could also use PolicyFactory (without caching)
|
||||
this.factory = new CachedPolicyFactory<ClassBehaviourBinding, P>(policyClass, index);
|
||||
this.dictionary = dictionary;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Policy implementation for the specified Class
|
||||
*
|
||||
* When multiple behaviours are bound to the policy for the class, an
|
||||
* aggregate policy implementation is returned which invokes each policy
|
||||
* in turn.
|
||||
*
|
||||
* @param classQName the class qualified name
|
||||
* @return the policy
|
||||
*/
|
||||
public P get(QName classQName)
|
||||
{
|
||||
return get(null, classQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Policy implementation for the specified Class
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the class name
|
||||
* @return the policy
|
||||
*/
|
||||
public P get(NodeRef nodeRef, QName classQName)
|
||||
{
|
||||
ClassDefinition classDefinition = dictionary.getClass(classQName);
|
||||
if (classDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Class " + classQName + " has not been defined in the data dictionary");
|
||||
}
|
||||
return factory.create(new ClassBehaviourBinding(dictionary, nodeRef, classQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of Policy implementations for the specified Class
|
||||
*
|
||||
* @param classQName the class qualified name
|
||||
* @return the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(QName classQName)
|
||||
{
|
||||
return getList(null, classQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of Policy implementations for the specified Class
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the class qualified name
|
||||
* @return the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(NodeRef nodeRef, QName classQName)
|
||||
{
|
||||
ClassDefinition classDefinition = dictionary.getClass(classQName);
|
||||
if (classDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Class " + classQName + " has not been defined in the data dictionary");
|
||||
}
|
||||
return factory.createList(new ClassBehaviourBinding(dictionary, nodeRef, classQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the policy implementation for the given classes. The single <tt>Policy</tt>
|
||||
* will be a wrapper of multiple appropriate policies.
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @return Returns the policy
|
||||
*/
|
||||
public P get(Set<QName> classQNames)
|
||||
{
|
||||
return get(null, classQNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the policy implementation for the given classes. The single <tt>Policy</tt>
|
||||
* will be a wrapper of multiple appropriate policies.
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQNames the class qualified names
|
||||
* @return Returns the policy
|
||||
*/
|
||||
public P get(NodeRef nodeRef, Set<QName> classQNames)
|
||||
{
|
||||
return factory.toPolicy(getList(nodeRef, classQNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of <tt>Policy</tt> implementations for the given classes
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @return Returns the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(Set<QName> classQNames)
|
||||
{
|
||||
return getList(null, classQNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of <tt>Policy</tt> implementations for the given classes
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @return Returns the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(NodeRef nodeRef, Set<QName> classQNames)
|
||||
{
|
||||
Collection<P> policies = new HashSet<P>();
|
||||
for (QName classQName : classQNames)
|
||||
{
|
||||
P policy = factory.create(new ClassBehaviourBinding(dictionary, nodeRef, classQName));
|
||||
if (policy instanceof PolicyList)
|
||||
{
|
||||
policies.addAll(((PolicyList<P>)policy).getPolicies());
|
||||
}
|
||||
else
|
||||
{
|
||||
policies.add(policy);
|
||||
}
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
}
|
261
source/java/org/alfresco/repo/policy/JavaBehaviour.java
Normal file
261
source/java/org/alfresco/repo/policy/JavaBehaviour.java
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* Java based Behaviour.
|
||||
*
|
||||
* A behavior acts like a delegate (a method pointer). The pointer is
|
||||
* represented by an instance object and method name.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class JavaBehaviour implements Behaviour
|
||||
{
|
||||
// The object instance holding the method
|
||||
private Object instance;
|
||||
|
||||
// The method name
|
||||
private String method;
|
||||
|
||||
// Cache of interface proxies (by interface class)
|
||||
private Map<Class, Object> proxies = new HashMap<Class, Object>();
|
||||
|
||||
// Enable / Disable invocation of behaviour
|
||||
private StackThreadLocal disabled = new StackThreadLocal();
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param instance the object instance holding the method
|
||||
* @param method the method name
|
||||
*/
|
||||
public JavaBehaviour(Object instance, String method)
|
||||
{
|
||||
ParameterCheck.mandatory("Instance", instance);
|
||||
ParameterCheck.mandatory("Method", method);
|
||||
this.instance = instance;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.Behaviour#getInterface(java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized <T> T getInterface(Class<T> 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<Integer> stack = disabled.get();
|
||||
stack.push(hashCode());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.Behaviour#enable()
|
||||
*/
|
||||
public void enable()
|
||||
{
|
||||
Stack<Integer> 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<Integer> stack = disabled.get();
|
||||
return stack.search(hashCode()) == -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Java method[class=" + instance.getClass().getName() + ", method=" + method + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Invocation Handler.
|
||||
*
|
||||
* @param <T> the policy interface class
|
||||
* @param instance the object instance
|
||||
* @param method the method name
|
||||
* @param policyIF the policy interface class
|
||||
* @return the invocation handler
|
||||
*/
|
||||
private <T> InvocationHandler getInvocationHandler(Object instance, String method, Class<T> policyIF)
|
||||
{
|
||||
Method[] policyIFMethods = policyIF.getMethods();
|
||||
if (policyIFMethods.length != 1)
|
||||
{
|
||||
throw new PolicyException("Policy interface " + policyIF.getCanonicalName() + " must have only one method");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Class instanceClass = instance.getClass();
|
||||
Method delegateMethod = instanceClass.getMethod(method, (Class[])policyIFMethods[0].getParameterTypes());
|
||||
return new JavaMethodInvocationHandler(this, delegateMethod);
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
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<Stack<Integer>>
|
||||
{
|
||||
@Override
|
||||
protected Stack<Integer> initialValue()
|
||||
{
|
||||
return new Stack<Integer>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Java Method Invocation Handler
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
private static class JavaMethodInvocationHandler implements InvocationHandler
|
||||
{
|
||||
private JavaBehaviour behaviour;
|
||||
private Method delegateMethod;
|
||||
|
||||
/**
|
||||
* Constuct.
|
||||
*
|
||||
* @param instance the object instance holding the method
|
||||
* @param delegateMethod the method to invoke
|
||||
*/
|
||||
private JavaMethodInvocationHandler(JavaBehaviour behaviour, Method delegateMethod)
|
||||
{
|
||||
this.behaviour = behaviour;
|
||||
this.delegateMethod = delegateMethod;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @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 delegateMethod.invoke(behaviour.instance, args);
|
||||
}
|
||||
catch (InvocationTargetException e)
|
||||
{
|
||||
throw e.getCause();
|
||||
}
|
||||
finally
|
||||
{
|
||||
behaviour.enable();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj == null || !(obj instanceof JavaMethodInvocationHandler))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
JavaMethodInvocationHandler other = (JavaMethodInvocationHandler)obj;
|
||||
return behaviour.instance.equals(other.behaviour.instance) && delegateMethod.equals(other.delegateMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return 37 * behaviour.instance.hashCode() + delegateMethod.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "JavaBehaviour[instance=" + behaviour.instance.hashCode() + ", method=" + delegateMethod.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
33
source/java/org/alfresco/repo/policy/Policy.java
Normal file
33
source/java/org/alfresco/repo/policy/Policy.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
|
||||
/**
|
||||
* Marker interface for representing a Policy.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public interface Policy
|
||||
{
|
||||
/**
|
||||
* mandatory static field on a <tt>Policy</tt> that can be overridden in
|
||||
* derived policies
|
||||
*/
|
||||
static String NAMESPACE = NamespaceService.ALFRESCO_URI;
|
||||
}
|
178
source/java/org/alfresco/repo/policy/PolicyComponent.java
Normal file
178
source/java/org/alfresco/repo/policy/PolicyComponent.java
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Policy Component for managing Policies and Behaviours.
|
||||
*
|
||||
* This component provides the ability to:
|
||||
*
|
||||
* a) Register policies
|
||||
* b) Bind behaviours to policies
|
||||
* c) Invoke policy behaviours
|
||||
*
|
||||
* A behaviour may be bound to a Policy before the Policy is registered. In
|
||||
* this case, the behaviour is not validated (i.e. checked to determine if it
|
||||
* supports the policy interface) until the Policy is registered. Otherwise,
|
||||
* the behaviour is validated at bind-time.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public interface PolicyComponent
|
||||
{
|
||||
/**
|
||||
* Register a Class-level Policy
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
* @param policy the policy interface class
|
||||
* @return A delegate for the class-level policy (typed by the policy interface)
|
||||
*/
|
||||
public <P extends ClassPolicy> ClassPolicyDelegate<P> registerClassPolicy(Class<P> policy);
|
||||
|
||||
/**
|
||||
* Register a Property-level Policy
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
* @param policy the policy interface class
|
||||
* @return A delegate for the property-level policy (typed by the policy interface)
|
||||
*/
|
||||
public <P extends PropertyPolicy> PropertyPolicyDelegate<P> registerPropertyPolicy(Class<P> policy);
|
||||
|
||||
/**
|
||||
* Register a Association-level Policy
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
* @param policy the policy interface class
|
||||
* @return A delegate for the association-level policy (typed by the policy interface)
|
||||
*/
|
||||
public <P extends AssociationPolicy> AssociationPolicyDelegate<P> registerAssociationPolicy(Class<P> policy);
|
||||
|
||||
/**
|
||||
* Gets all registered Policies
|
||||
*
|
||||
* @return the collection of registered policy definitions
|
||||
*/
|
||||
public Collection<PolicyDefinition> getRegisteredPolicies();
|
||||
|
||||
/**
|
||||
* Gets the specified registered Policy
|
||||
*
|
||||
* @param policyType the policy type
|
||||
* @param policy the policy name
|
||||
* @return the policy definition (or null, if it has not been registered)
|
||||
*/
|
||||
public PolicyDefinition getRegisteredPolicy(PolicyType policyType, QName policy);
|
||||
|
||||
/**
|
||||
* Determine if the specified policy has been registered
|
||||
*
|
||||
* @param policyType the policy type
|
||||
* @param policy the policy name
|
||||
* @return true => registered, false => not yet
|
||||
*/
|
||||
public boolean isRegisteredPolicy(PolicyType policyType, QName policy);
|
||||
|
||||
/**
|
||||
* Bind a Class specific behaviour to a Class-level Policy
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ClassBehaviourBinding> bindClassBehaviour(QName policy, QName classRef, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind a Service behaviour to a Class-level Policy
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param service the service (any object, in fact)
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ServiceBehaviourBinding> bindClassBehaviour(QName policy, Object service, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind a Property specific behaviour to a Property-level Policy
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param className the class to bind against
|
||||
* @param propertyName the property to bind against
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindPropertyBehaviour(QName policy, QName className, QName propertyName, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind a Property specific behaviour to a Property-level Policy (for all properties of a Class)
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param className the class to bind against
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindPropertyBehaviour(QName policy, QName className, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind a Service specific behaviour to a Property-level Policy
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param service the binding service
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ServiceBehaviourBinding> bindPropertyBehaviour(QName policy, Object service, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind an Association specific behaviour to an Association-level Policy
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param className the class to bind against
|
||||
* @param assocRef the association to bind against
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindAssociationBehaviour(QName policy, QName className, QName assocName, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind an Association specific behaviour to an Association-level Policy (for all associations of a Class)
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param className the class to bind against
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindAssociationBehaviour(QName policy, QName className, Behaviour behaviour);
|
||||
|
||||
/**
|
||||
* Bind a Service specific behaviour to an Association-level Policy
|
||||
*
|
||||
* @param policy the policy name
|
||||
* @param service the binding service
|
||||
* @param behaviour the behaviour
|
||||
* @return the registered behaviour definition
|
||||
*/
|
||||
public BehaviourDefinition<ServiceBehaviourBinding> bindAssociationBehaviour(QName policy, Object service, Behaviour behaviour);
|
||||
|
||||
}
|
||||
|
||||
|
666
source/java/org/alfresco/repo/policy/PolicyComponentImpl.java
Normal file
666
source/java/org/alfresco/repo/policy/PolicyComponentImpl.java
Normal file
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Policy Component Implementation.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class PolicyComponentImpl implements PolicyComponent
|
||||
{
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(PolicyComponentImpl.class);
|
||||
|
||||
// Policy interface annotations
|
||||
private static String ANNOTATION_NAMESPACE = "NAMESPACE";
|
||||
|
||||
// Dictionary Service
|
||||
private DictionaryService dictionary;
|
||||
|
||||
// Behaviour Filter
|
||||
private BehaviourFilter behaviourFilter;
|
||||
|
||||
// Map of registered Policies
|
||||
private Map<PolicyKey, PolicyDefinition> registeredPolicies;;
|
||||
|
||||
// Map of Class Behaviours (by policy name)
|
||||
private Map<QName, ClassBehaviourIndex<ClassBehaviourBinding>> classBehaviours = new HashMap<QName, ClassBehaviourIndex<ClassBehaviourBinding>>();
|
||||
|
||||
// Map of Property Behaviours (by policy name)
|
||||
private Map<QName, ClassBehaviourIndex<ClassFeatureBehaviourBinding>> propertyBehaviours = new HashMap<QName, ClassBehaviourIndex<ClassFeatureBehaviourBinding>>();
|
||||
|
||||
// Map of Association Behaviours (by policy name)
|
||||
private Map<QName, ClassBehaviourIndex<ClassFeatureBehaviourBinding>> associationBehaviours = new HashMap<QName, ClassBehaviourIndex<ClassFeatureBehaviourBinding>>();
|
||||
|
||||
// Wild Card Feature
|
||||
private static final QName FEATURE_WILDCARD = QName.createQName(NamespaceService.DEFAULT_URI, "*");
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param dictionary dictionary service
|
||||
* @param behaviourFilter behaviour filter
|
||||
*/
|
||||
public PolicyComponentImpl(DictionaryService dictionary)
|
||||
{
|
||||
this.dictionary = dictionary;
|
||||
this.registeredPolicies = new HashMap<PolicyKey, PolicyDefinition>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the behaviour filter
|
||||
*
|
||||
* @param filter
|
||||
*/
|
||||
public void setBehaviourFilter(BehaviourFilter filter)
|
||||
{
|
||||
this.behaviourFilter = filter;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#registerClassPolicy()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P extends ClassPolicy> ClassPolicyDelegate<P> registerClassPolicy(Class<P> policy)
|
||||
{
|
||||
ParameterCheck.mandatory("Policy interface class", policy);
|
||||
PolicyDefinition definition = createPolicyDefinition(policy);
|
||||
registeredPolicies.put(new PolicyKey(definition.getType(), definition.getName()), definition);
|
||||
ClassPolicyDelegate<P> delegate = new ClassPolicyDelegate<P>(dictionary, policy, getClassBehaviourIndex(definition.getName()));
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Registered class policy " + definition.getName() + " (" + definition.getPolicyInterface() + ")");
|
||||
|
||||
return delegate;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#registerPropertyPolicy(java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P extends PropertyPolicy> PropertyPolicyDelegate<P> registerPropertyPolicy(Class<P> policy)
|
||||
{
|
||||
ParameterCheck.mandatory("Policy interface class", policy);
|
||||
PolicyDefinition definition = createPolicyDefinition(policy);
|
||||
registeredPolicies.put(new PolicyKey(definition.getType(), definition.getName()), definition);
|
||||
PropertyPolicyDelegate<P> delegate = new PropertyPolicyDelegate<P>(dictionary, policy, getPropertyBehaviourIndex(definition.getName()));
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Registered property policy " + definition.getName() + " (" + definition.getPolicyInterface() + ")");
|
||||
|
||||
return delegate;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#registerAssociationPolicy(java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P extends AssociationPolicy> AssociationPolicyDelegate<P> registerAssociationPolicy(Class<P> policy)
|
||||
{
|
||||
ParameterCheck.mandatory("Policy interface class", policy);
|
||||
PolicyDefinition definition = createPolicyDefinition(policy);
|
||||
registeredPolicies.put(new PolicyKey(definition.getType(), definition.getName()), definition);
|
||||
AssociationPolicyDelegate<P> delegate = new AssociationPolicyDelegate<P>(dictionary, policy, getAssociationBehaviourIndex(definition.getName()));
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Registered association policy " + definition.getName() + " (" + definition.getPolicyInterface() + ")");
|
||||
|
||||
return delegate;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#getRegisteredPolicies()
|
||||
*/
|
||||
public Collection<PolicyDefinition> getRegisteredPolicies()
|
||||
{
|
||||
return Collections.unmodifiableCollection(registeredPolicies.values());
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#getRegisteredPolicy(org.alfresco.repo.policy.PolicyType, org.alfresco.repo.ref.QName)
|
||||
*/
|
||||
public PolicyDefinition getRegisteredPolicy(PolicyType policyType, QName policy)
|
||||
{
|
||||
return registeredPolicies.get(new PolicyKey(policyType, policy));
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#isRegisteredPolicy(org.alfresco.repo.policy.PolicyType, org.alfresco.repo.ref.QName)
|
||||
*/
|
||||
public boolean isRegisteredPolicy(PolicyType policyType, QName policy)
|
||||
{
|
||||
return registeredPolicies.containsKey(new PolicyKey(policyType, policy));
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindClassBehaviour(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ClassBehaviourBinding> bindClassBehaviour(QName policy, QName classRef, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Class Reference", classRef);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Validate Binding
|
||||
ClassDefinition classDefinition = dictionary.getClass(classRef);
|
||||
if (classDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Class " + classRef + " has not been defined in the data dictionary");
|
||||
}
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ClassBehaviourBinding binding = new ClassBehaviourBinding(dictionary, classRef);
|
||||
BehaviourDefinition<ClassBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Class, policy, binding, behaviour);
|
||||
getClassBehaviourIndex(policy).putClassBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to policy " + policy + " for class " + classRef);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindClassBehaviour(org.alfresco.repo.ref.QName, java.lang.Object, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ServiceBehaviourBinding> bindClassBehaviour(QName policy, Object service, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Service", service);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ServiceBehaviourBinding binding = new ServiceBehaviourBinding(service);
|
||||
BehaviourDefinition<ServiceBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Class, policy, binding, behaviour);
|
||||
getClassBehaviourIndex(policy).putServiceBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to policy " + policy + " for service " + service);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindPropertyBehaviour(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindPropertyBehaviour(QName policy, QName className, QName propertyName, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Class Reference", className);
|
||||
ParameterCheck.mandatory("Property Reference", propertyName);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Validate Binding
|
||||
PropertyDefinition propertyDefinition = dictionary.getProperty(className, propertyName);
|
||||
if (propertyDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Property " + propertyName + " of class " + className + " has not been defined in the data dictionary");
|
||||
}
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ClassFeatureBehaviourBinding binding = new ClassFeatureBehaviourBinding(dictionary, className, propertyName);
|
||||
BehaviourDefinition<ClassFeatureBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Property, policy, binding, behaviour);
|
||||
getPropertyBehaviourIndex(policy).putClassBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to policy " + policy + " for property " + propertyName + " of class " + className);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindPropertyBehaviour(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindPropertyBehaviour(QName policy, QName className, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Class Reference", className);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Validate Binding
|
||||
ClassDefinition classDefinition = dictionary.getClass(className);
|
||||
if (classDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Class " + className + " has not been defined in the data dictionary");
|
||||
}
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ClassFeatureBehaviourBinding binding = new ClassFeatureBehaviourBinding(dictionary, className, FEATURE_WILDCARD);
|
||||
BehaviourDefinition<ClassFeatureBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Property, policy, binding, behaviour);
|
||||
getPropertyBehaviourIndex(policy).putClassBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to policy " + policy + " for property " + FEATURE_WILDCARD + " of class " + className);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindPropertyBehaviour(org.alfresco.repo.ref.QName, java.lang.Object, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ServiceBehaviourBinding> bindPropertyBehaviour(QName policy, Object service, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Service", service);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ServiceBehaviourBinding binding = new ServiceBehaviourBinding(service);
|
||||
BehaviourDefinition<ServiceBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Property, policy, binding, behaviour);
|
||||
getPropertyBehaviourIndex(policy).putServiceBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to property policy " + policy + " for service " + service);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindAssociationBehaviour(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindAssociationBehaviour(QName policy, QName className, QName assocName, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Class Reference", className);
|
||||
ParameterCheck.mandatory("Association Reference", assocName);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Validate Binding
|
||||
AssociationDefinition assocDefinition = dictionary.getAssociation(assocName);
|
||||
if (assocDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Association " + assocName + " of class " + className + " has not been defined in the data dictionary");
|
||||
}
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ClassFeatureBehaviourBinding binding = new ClassFeatureBehaviourBinding(dictionary, className, assocName);
|
||||
BehaviourDefinition<ClassFeatureBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Association, policy, binding, behaviour);
|
||||
getAssociationBehaviourIndex(policy).putClassBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to policy " + policy + " for association " + assocName + " of class " + className);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindAssociationBehaviour(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ClassFeatureBehaviourBinding> bindAssociationBehaviour(QName policy, QName className, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Class Reference", className);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Validate Binding
|
||||
ClassDefinition classDefinition = dictionary.getClass(className);
|
||||
if (classDefinition == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Class " + className + " has not been defined in the data dictionary");
|
||||
}
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ClassFeatureBehaviourBinding binding = new ClassFeatureBehaviourBinding(dictionary, className, FEATURE_WILDCARD);
|
||||
BehaviourDefinition<ClassFeatureBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Association, policy, binding, behaviour);
|
||||
getAssociationBehaviourIndex(policy).putClassBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to policy " + policy + " for association " + FEATURE_WILDCARD + " of class " + className);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyComponent#bindAssociationBehaviour(org.alfresco.repo.ref.QName, java.lang.Object, org.alfresco.repo.policy.Behaviour)
|
||||
*/
|
||||
public BehaviourDefinition<ServiceBehaviourBinding> bindAssociationBehaviour(QName policy, Object service, Behaviour behaviour)
|
||||
{
|
||||
// Validate arguments
|
||||
ParameterCheck.mandatory("Policy", policy);
|
||||
ParameterCheck.mandatory("Service", service);
|
||||
ParameterCheck.mandatory("Behaviour", behaviour);
|
||||
|
||||
// Create behaviour definition and bind to policy
|
||||
ServiceBehaviourBinding binding = new ServiceBehaviourBinding(service);
|
||||
BehaviourDefinition<ServiceBehaviourBinding> definition = createBehaviourDefinition(PolicyType.Association, policy, binding, behaviour);
|
||||
getAssociationBehaviourIndex(policy).putServiceBehaviour(definition);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Bound " + behaviour + " to association policy " + policy + " for service " + service);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Class behaviour index for the specified Policy
|
||||
*
|
||||
* @param policy the policy
|
||||
* @return the class behaviour index
|
||||
*/
|
||||
private synchronized ClassBehaviourIndex<ClassBehaviourBinding> getClassBehaviourIndex(QName policy)
|
||||
{
|
||||
ClassBehaviourIndex<ClassBehaviourBinding> index = classBehaviours.get(policy);
|
||||
if (index == null)
|
||||
{
|
||||
index = new ClassBehaviourIndex<ClassBehaviourBinding>(behaviourFilter);
|
||||
classBehaviours.put(policy, index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Property behaviour index for the specified Policy
|
||||
*
|
||||
* @param policy the policy
|
||||
* @return the property behaviour index
|
||||
*/
|
||||
private synchronized ClassBehaviourIndex<ClassFeatureBehaviourBinding> getPropertyBehaviourIndex(QName policy)
|
||||
{
|
||||
ClassBehaviourIndex<ClassFeatureBehaviourBinding> index = propertyBehaviours.get(policy);
|
||||
if (index == null)
|
||||
{
|
||||
index = new ClassBehaviourIndex<ClassFeatureBehaviourBinding>(behaviourFilter);
|
||||
propertyBehaviours.put(policy, index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Association behaviour index for the specified Policy
|
||||
*
|
||||
* @param policy the policy
|
||||
* @return the association behaviour index
|
||||
*/
|
||||
private synchronized ClassBehaviourIndex<ClassFeatureBehaviourBinding> getAssociationBehaviourIndex(QName policy)
|
||||
{
|
||||
ClassBehaviourIndex<ClassFeatureBehaviourBinding> index = associationBehaviours.get(policy);
|
||||
if (index == null)
|
||||
{
|
||||
index = new ClassBehaviourIndex<ClassFeatureBehaviourBinding>(behaviourFilter);
|
||||
associationBehaviours.put(policy, index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a Behaviour Definition
|
||||
*
|
||||
* @param <B> the type of binding
|
||||
* @param type policy type
|
||||
* @param policy policy name
|
||||
* @param binding the binding
|
||||
* @param behaviour the behaviour
|
||||
* @return the behaviour definition
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <B extends BehaviourBinding> BehaviourDefinition<B> createBehaviourDefinition(PolicyType type, QName policy, B binding, Behaviour behaviour)
|
||||
{
|
||||
// Determine if policy has already been registered
|
||||
PolicyDefinition policyDefinition = getRegisteredPolicy(type, policy);
|
||||
if (policyDefinition != null)
|
||||
{
|
||||
// Policy has already been registered, force validation of behaviour now
|
||||
behaviour.getInterface(policyDefinition.getPolicyInterface());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Behaviour " + behaviour + " is binding (" + binding + ") to policy " + policy + " before the policy is registered");
|
||||
}
|
||||
|
||||
// Construct the definition
|
||||
return new BehaviourDefinitionImpl<B>(type, policy, binding, behaviour);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a Policy Definition
|
||||
*
|
||||
* @param policyIF the policy interface
|
||||
* @return the policy definition
|
||||
*/
|
||||
private PolicyDefinition createPolicyDefinition(Class policyIF)
|
||||
{
|
||||
// Extract Policy Namespace
|
||||
String namespaceURI = NamespaceService.DEFAULT_URI;
|
||||
try
|
||||
{
|
||||
Field metadata = policyIF.getField(ANNOTATION_NAMESPACE);
|
||||
if (!String.class.isAssignableFrom(metadata.getType()))
|
||||
{
|
||||
throw new PolicyException("NAMESPACE metadata incorrectly specified in policy " + policyIF.getCanonicalName());
|
||||
}
|
||||
namespaceURI = (String)metadata.get(null);
|
||||
}
|
||||
catch(NoSuchFieldException e)
|
||||
{
|
||||
// Assume default namespace
|
||||
}
|
||||
catch(IllegalAccessException e)
|
||||
{
|
||||
// Shouldn't get here (interface definitions must be accessible)
|
||||
}
|
||||
|
||||
// Extract Policy Name
|
||||
Method[] methods = policyIF.getMethods();
|
||||
if (methods.length != 1)
|
||||
{
|
||||
throw new PolicyException("Policy " + policyIF.getCanonicalName() + " must declare only one method");
|
||||
}
|
||||
String name = methods[0].getName();
|
||||
|
||||
// Create Policy Definition
|
||||
return new PolicyDefinitionImpl(QName.createQName(namespaceURI, name), policyIF);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Policy Key (composite of policy type and name)
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
private static class PolicyKey
|
||||
{
|
||||
private PolicyType type;
|
||||
private QName policy;
|
||||
|
||||
private PolicyKey(PolicyType type, QName policy)
|
||||
{
|
||||
this.type = type;
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj == null || !(obj instanceof PolicyKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PolicyKey other = (PolicyKey)obj;
|
||||
return type.equals(other.type) && policy.equals(other.policy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return 37 * type.hashCode() + policy.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Policy Definition implementation.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
/*package*/ class PolicyDefinitionImpl implements PolicyDefinition
|
||||
{
|
||||
private QName policy;
|
||||
private Class policyIF;
|
||||
|
||||
/*package*/ PolicyDefinitionImpl(QName policy, Class policyIF)
|
||||
{
|
||||
this.policy = policy;
|
||||
this.policyIF = policyIF;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyDefinition#getName()
|
||||
*/
|
||||
public QName getName()
|
||||
{
|
||||
return policy;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyDefinition#getPolicyInterface()
|
||||
*/
|
||||
public Class getPolicyInterface()
|
||||
{
|
||||
return policyIF;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyDefinition#getType()
|
||||
*/
|
||||
public PolicyType getType()
|
||||
{
|
||||
if (ClassPolicy.class.isAssignableFrom(policyIF))
|
||||
{
|
||||
return PolicyType.Class;
|
||||
}
|
||||
else if (PropertyPolicy.class.isAssignableFrom(policyIF))
|
||||
{
|
||||
return PolicyType.Property;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PolicyType.Association;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Behaviour Definition implementation.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> the type of binding
|
||||
*/
|
||||
/*package*/ class BehaviourDefinitionImpl<B extends BehaviourBinding> implements BehaviourDefinition<B>
|
||||
{
|
||||
private PolicyType type;
|
||||
private QName policy;
|
||||
private B binding;
|
||||
private Behaviour behaviour;
|
||||
|
||||
/*package*/ BehaviourDefinitionImpl(PolicyType type, QName policy, B binding, Behaviour behaviour)
|
||||
{
|
||||
this.type = type;
|
||||
this.policy = policy;
|
||||
this.binding = binding;
|
||||
this.behaviour = behaviour;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourDefinition#getPolicy()
|
||||
*/
|
||||
public QName getPolicy()
|
||||
{
|
||||
return policy;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourDefinition#getPolicyDefinition()
|
||||
*/
|
||||
public PolicyDefinition getPolicyDefinition()
|
||||
{
|
||||
return getRegisteredPolicy(type, policy);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourDefinition#getBinding()
|
||||
*/
|
||||
public B getBinding()
|
||||
{
|
||||
return binding;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourDefinition#getBehaviour()
|
||||
*/
|
||||
public Behaviour getBehaviour()
|
||||
{
|
||||
return behaviour;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
588
source/java/org/alfresco/repo/policy/PolicyComponentTest.java
Normal file
588
source/java/org/alfresco/repo/policy/PolicyComponentTest.java
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.repo.dictionary.DictionaryBootstrap;
|
||||
import org.alfresco.repo.dictionary.DictionaryComponent;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAOImpl;
|
||||
import org.alfresco.repo.dictionary.NamespaceDAO;
|
||||
import org.alfresco.repo.dictionary.NamespaceDAOImpl;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
public class PolicyComponentTest extends TestCase
|
||||
{
|
||||
private static final String TEST_MODEL = "org/alfresco/repo/policy/policycomponenttest_model.xml";
|
||||
private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/policycomponenttest/1.0";
|
||||
private static QName BASE_TYPE = QName.createQName(TEST_NAMESPACE, "base");
|
||||
private static QName BASE_PROP_A = QName.createQName(TEST_NAMESPACE, "base_a");
|
||||
private static QName BASE_ASSOC_A = QName.createQName(TEST_NAMESPACE, "base_assoc_a");
|
||||
private static QName FILE_TYPE = QName.createQName(TEST_NAMESPACE, "file");
|
||||
private static QName FILE_PROP_B = QName.createQName(TEST_NAMESPACE, "file_b");
|
||||
private static QName FOLDER_TYPE = QName.createQName(TEST_NAMESPACE, "folder");
|
||||
private static QName FOLDER_PROP_D = QName.createQName(TEST_NAMESPACE, "folder_d");
|
||||
private static QName TEST_ASPECT = QName.createQName(TEST_NAMESPACE, "aspect");
|
||||
private static QName ASPECT_PROP_A = QName.createQName(TEST_NAMESPACE, "aspect_a");
|
||||
private static QName INVALID_TYPE = QName.createQName(TEST_NAMESPACE, "classdoesnotexist");
|
||||
|
||||
private PolicyComponent policyComponent = null;
|
||||
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
// Instantiate Dictionary Service
|
||||
NamespaceDAO namespaceDAO = new NamespaceDAOImpl();
|
||||
DictionaryDAOImpl dictionaryDAO = new DictionaryDAOImpl(namespaceDAO);
|
||||
|
||||
DictionaryBootstrap bootstrap = new DictionaryBootstrap();
|
||||
List<String> bootstrapModels = new ArrayList<String>();
|
||||
bootstrapModels.add("alfresco/model/dictionaryModel.xml");
|
||||
bootstrapModels.add("org/alfresco/repo/policy/policycomponenttest_model.xml");
|
||||
bootstrapModels.add(TEST_MODEL);
|
||||
bootstrap.setModels(bootstrapModels);
|
||||
bootstrap.setDictionaryDAO(dictionaryDAO);
|
||||
bootstrap.bootstrap();
|
||||
|
||||
DictionaryComponent dictionary = new DictionaryComponent();
|
||||
dictionary.setDictionaryDAO(dictionaryDAO);
|
||||
|
||||
// Instantiate Policy Component
|
||||
policyComponent = new PolicyComponentImpl(dictionary);
|
||||
}
|
||||
|
||||
|
||||
public void testJavaBehaviour()
|
||||
{
|
||||
Behaviour validBehaviour = new JavaBehaviour(this, "validTest");
|
||||
TestClassPolicy policy = validBehaviour.getInterface(TestClassPolicy.class);
|
||||
assertNotNull(policy);
|
||||
String result = policy.test("argument");
|
||||
assertEquals("ValidTest: argument", result);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testRegisterDefinitions()
|
||||
{
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("unused") ClassPolicyDelegate<InvalidMetaDataPolicy> delegate = policyComponent.registerClassPolicy(InvalidMetaDataPolicy.class);
|
||||
fail("Failed to catch hidden metadata");
|
||||
}
|
||||
catch(PolicyException e)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("unused") ClassPolicyDelegate<NoMethodPolicy> delegate = policyComponent.registerClassPolicy(NoMethodPolicy.class);
|
||||
fail("Failed to catch no methods defined in policy");
|
||||
}
|
||||
catch(PolicyException e)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("unused") ClassPolicyDelegate<MultiMethodPolicy> delegate = policyComponent.registerClassPolicy(MultiMethodPolicy.class);
|
||||
fail("Failed to catch multiple methods defined in policy");
|
||||
}
|
||||
catch(PolicyException e)
|
||||
{
|
||||
}
|
||||
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
boolean isRegistered = policyComponent.isRegisteredPolicy(PolicyType.Class, policyName);
|
||||
assertFalse(isRegistered);
|
||||
ClassPolicyDelegate<TestClassPolicy> delegate = policyComponent.registerClassPolicy(TestClassPolicy.class);
|
||||
assertNotNull(delegate);
|
||||
isRegistered = policyComponent.isRegisteredPolicy(PolicyType.Class, policyName);
|
||||
assertTrue(isRegistered);
|
||||
PolicyDefinition definition = policyComponent.getRegisteredPolicy(PolicyType.Class, policyName);
|
||||
assertNotNull(definition);
|
||||
assertEquals(policyName, definition.getName());
|
||||
assertEquals(PolicyType.Class, definition.getType());
|
||||
assertEquals(TestClassPolicy.class, definition.getPolicyInterface());
|
||||
}
|
||||
|
||||
|
||||
public void testBindBehaviour()
|
||||
{
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour validBehaviour = new JavaBehaviour(this, "validTest");
|
||||
|
||||
// Test null policy
|
||||
try
|
||||
{
|
||||
policyComponent.bindClassBehaviour(null, FILE_TYPE, validBehaviour);
|
||||
fail("Failed to catch null policy whilst binding behaviour");
|
||||
}
|
||||
catch(IllegalArgumentException e) {}
|
||||
|
||||
// Test null Class Reference
|
||||
try
|
||||
{
|
||||
policyComponent.bindClassBehaviour(policyName, null, validBehaviour);
|
||||
fail("Failed to catch null class reference whilst binding behaviour");
|
||||
}
|
||||
catch(IllegalArgumentException e) {}
|
||||
|
||||
// Test invalid Class Reference
|
||||
try
|
||||
{
|
||||
policyComponent.bindClassBehaviour(policyName, INVALID_TYPE, validBehaviour);
|
||||
fail("Failed to catch invalid class reference whilst binding behaviour");
|
||||
}
|
||||
catch(IllegalArgumentException e) {}
|
||||
|
||||
// Test null Behaviour
|
||||
try
|
||||
{
|
||||
policyComponent.bindClassBehaviour(policyName, FILE_TYPE, null);
|
||||
fail("Failed to catch null behaviour whilst binding behaviour");
|
||||
}
|
||||
catch(IllegalArgumentException e) {}
|
||||
|
||||
// Test invalid behaviour (for registered policy)
|
||||
Behaviour invalidBehaviour = new JavaBehaviour(this, "methoddoesnotexist");
|
||||
policyComponent.registerClassPolicy(TestClassPolicy.class);
|
||||
try
|
||||
{
|
||||
policyComponent.bindClassBehaviour(policyName, FILE_TYPE, invalidBehaviour);
|
||||
fail("Failed to catch invalid behaviour whilst binding behaviour");
|
||||
}
|
||||
catch(PolicyException e) {}
|
||||
|
||||
// Test valid behaviour (for registered policy)
|
||||
try
|
||||
{
|
||||
BehaviourDefinition<ClassBehaviourBinding> definition = policyComponent.bindClassBehaviour(policyName, FILE_TYPE, validBehaviour);
|
||||
assertNotNull(definition);
|
||||
assertEquals(policyName, definition.getPolicy());
|
||||
assertEquals(FILE_TYPE, definition.getBinding().getClassQName());
|
||||
}
|
||||
catch(PolicyException e)
|
||||
{
|
||||
fail("Policy exception thrown for valid behaviour");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testClassDelegate()
|
||||
{
|
||||
// Register Policy
|
||||
ClassPolicyDelegate<TestClassPolicy> delegate = policyComponent.registerClassPolicy(TestClassPolicy.class);
|
||||
|
||||
// Bind Class Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour fileBehaviour = new JavaBehaviour(this, "fileTest");
|
||||
policyComponent.bindClassBehaviour(policyName, FILE_TYPE, fileBehaviour);
|
||||
|
||||
// Test NOOP Policy delegate
|
||||
Collection<TestClassPolicy> basePolicies = delegate.getList(BASE_TYPE);
|
||||
assertNotNull(basePolicies);
|
||||
assertTrue(basePolicies.size() == 0);
|
||||
TestClassPolicy basePolicy = delegate.get(BASE_TYPE);
|
||||
assertNotNull(basePolicy);
|
||||
|
||||
// Test single Policy delegate
|
||||
Collection<TestClassPolicy> filePolicies = delegate.getList(FILE_TYPE);
|
||||
assertNotNull(filePolicies);
|
||||
assertTrue(filePolicies.size() == 1);
|
||||
TestClassPolicy filePolicy = delegate.get(FILE_TYPE);
|
||||
assertNotNull(filePolicy);
|
||||
assertEquals(filePolicies.iterator().next(), filePolicy);
|
||||
|
||||
// Bind Service Behaviour
|
||||
Behaviour serviceBehaviour = new JavaBehaviour(this, "serviceTest");
|
||||
policyComponent.bindClassBehaviour(policyName, this, serviceBehaviour);
|
||||
|
||||
// Test multi Policy delegate
|
||||
Collection<TestClassPolicy> file2Policies = delegate.getList(FILE_TYPE);
|
||||
assertNotNull(file2Policies);
|
||||
assertTrue(file2Policies.size() == 2);
|
||||
TestClassPolicy filePolicy2 = delegate.get(FILE_TYPE);
|
||||
assertNotNull(filePolicy2);
|
||||
}
|
||||
|
||||
|
||||
public void testClassOverride()
|
||||
{
|
||||
// Register Policy
|
||||
ClassPolicyDelegate<TestClassPolicy> delegate = policyComponent.registerClassPolicy(TestClassPolicy.class);
|
||||
|
||||
// Bind Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour baseBehaviour = new JavaBehaviour(this, "baseTest");
|
||||
policyComponent.bindClassBehaviour(policyName, BASE_TYPE, baseBehaviour);
|
||||
Behaviour folderBehaviour = new JavaBehaviour(this, "folderTest");
|
||||
policyComponent.bindClassBehaviour(policyName, FOLDER_TYPE, folderBehaviour);
|
||||
|
||||
// Invoke Policies
|
||||
TestClassPolicy basePolicy = delegate.get(BASE_TYPE);
|
||||
String baseResult = basePolicy.test("base");
|
||||
assertEquals("Base: base", baseResult);
|
||||
TestClassPolicy filePolicy = delegate.get(FILE_TYPE);
|
||||
String fileResult = filePolicy.test("file");
|
||||
assertEquals("Base: file", fileResult);
|
||||
TestClassPolicy folderPolicy = delegate.get(FOLDER_TYPE);
|
||||
String folderResult = folderPolicy.test("folder");
|
||||
assertEquals("Folder: folder", folderResult);
|
||||
}
|
||||
|
||||
|
||||
public void testClassCache()
|
||||
{
|
||||
// Register Policy
|
||||
ClassPolicyDelegate<TestClassPolicy> delegate = policyComponent.registerClassPolicy(TestClassPolicy.class);
|
||||
|
||||
// Bind Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour baseBehaviour = new JavaBehaviour(this, "baseTest");
|
||||
policyComponent.bindClassBehaviour(policyName, BASE_TYPE, baseBehaviour);
|
||||
Behaviour folderBehaviour = new JavaBehaviour(this, "folderTest");
|
||||
policyComponent.bindClassBehaviour(policyName, FOLDER_TYPE, folderBehaviour);
|
||||
|
||||
// Invoke Policies
|
||||
TestClassPolicy basePolicy = delegate.get(BASE_TYPE);
|
||||
String baseResult = basePolicy.test("base");
|
||||
assertEquals("Base: base", baseResult);
|
||||
TestClassPolicy filePolicy = delegate.get(FILE_TYPE);
|
||||
String fileResult = filePolicy.test("file");
|
||||
assertEquals("Base: file", fileResult);
|
||||
TestClassPolicy folderPolicy = delegate.get(FOLDER_TYPE);
|
||||
String folderResult = folderPolicy.test("folder");
|
||||
assertEquals("Folder: folder", folderResult);
|
||||
|
||||
// Retrieve delegates again
|
||||
TestClassPolicy basePolicy2 = delegate.get(BASE_TYPE);
|
||||
assertTrue(basePolicy == basePolicy2);
|
||||
TestClassPolicy filePolicy2 = delegate.get(FILE_TYPE);
|
||||
assertTrue(filePolicy == filePolicy2);
|
||||
TestClassPolicy folderPolicy2 = delegate.get(FOLDER_TYPE);
|
||||
assertTrue(folderPolicy == folderPolicy2);
|
||||
|
||||
// Bind new behaviour (forcing base & file cache resets)
|
||||
Behaviour newBaseBehaviour = new JavaBehaviour(this, "newBaseTest");
|
||||
policyComponent.bindClassBehaviour(policyName, BASE_TYPE, newBaseBehaviour);
|
||||
|
||||
// Invoke Policies
|
||||
TestClassPolicy basePolicy3 = delegate.get(BASE_TYPE);
|
||||
assertTrue(basePolicy3 != basePolicy2);
|
||||
String baseResult3 = basePolicy3.test("base");
|
||||
assertEquals("NewBase: base", baseResult3);
|
||||
TestClassPolicy filePolicy3 = delegate.get(FILE_TYPE);
|
||||
assertTrue(filePolicy3 != filePolicy2);
|
||||
String fileResult3 = filePolicy3.test("file");
|
||||
assertEquals("NewBase: file", fileResult3);
|
||||
TestClassPolicy folderPolicy3 = delegate.get(FOLDER_TYPE);
|
||||
assertTrue(folderPolicy3 == folderPolicy2);
|
||||
String folderResult3 = folderPolicy3.test("folder");
|
||||
assertEquals("Folder: folder", folderResult3);
|
||||
|
||||
// Bind new behaviour (forcing file cache reset)
|
||||
Behaviour fileBehaviour = new JavaBehaviour(this, "fileTest");
|
||||
policyComponent.bindClassBehaviour(policyName, FILE_TYPE, fileBehaviour);
|
||||
|
||||
// Invoke Policies
|
||||
TestClassPolicy basePolicy4 = delegate.get(BASE_TYPE);
|
||||
assertTrue(basePolicy4 == basePolicy3);
|
||||
String baseResult4 = basePolicy4.test("base");
|
||||
assertEquals("NewBase: base", baseResult4);
|
||||
TestClassPolicy filePolicy4 = delegate.get(FILE_TYPE);
|
||||
assertTrue(filePolicy4 != filePolicy3);
|
||||
String fileResult4 = filePolicy4.test("file");
|
||||
assertEquals("File: file", fileResult4);
|
||||
TestClassPolicy folderPolicy4 = delegate.get(FOLDER_TYPE);
|
||||
assertTrue(folderPolicy4 == folderPolicy4);
|
||||
String folderResult4 = folderPolicy4.test("folder");
|
||||
assertEquals("Folder: folder", folderResult4);
|
||||
}
|
||||
|
||||
|
||||
public void testPropertyDelegate()
|
||||
{
|
||||
// Register Policy
|
||||
PropertyPolicyDelegate<TestPropertyPolicy> delegate = policyComponent.registerPropertyPolicy(TestPropertyPolicy.class);
|
||||
|
||||
// Bind Property Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour fileBehaviour = new JavaBehaviour(this, "fileTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FILE_TYPE, FILE_PROP_B, fileBehaviour);
|
||||
|
||||
// Test NOOP Policy delegate
|
||||
Collection<TestPropertyPolicy> basePolicies = delegate.getList(BASE_TYPE, BASE_PROP_A);
|
||||
assertNotNull(basePolicies);
|
||||
assertTrue(basePolicies.size() == 0);
|
||||
TestPropertyPolicy basePolicy = delegate.get(BASE_TYPE, BASE_PROP_A);
|
||||
assertNotNull(basePolicy);
|
||||
|
||||
// Test single Policy delegate
|
||||
Collection<TestPropertyPolicy> filePolicies = delegate.getList(FILE_TYPE, FILE_PROP_B);
|
||||
assertNotNull(filePolicies);
|
||||
assertTrue(filePolicies.size() == 1);
|
||||
TestPropertyPolicy filePolicy = delegate.get(FILE_TYPE, FILE_PROP_B);
|
||||
assertNotNull(filePolicy);
|
||||
assertEquals(filePolicies.iterator().next(), filePolicy);
|
||||
|
||||
// Bind Service Behaviour
|
||||
Behaviour serviceBehaviour = new JavaBehaviour(this, "serviceTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, this, serviceBehaviour);
|
||||
|
||||
// Test multi Policy delegate
|
||||
Collection<TestPropertyPolicy> file2Policies = delegate.getList(FILE_TYPE, FILE_PROP_B);
|
||||
assertNotNull(file2Policies);
|
||||
assertTrue(file2Policies.size() == 2);
|
||||
TestPropertyPolicy filePolicy2 = delegate.get(FILE_TYPE, FILE_PROP_B);
|
||||
assertNotNull(filePolicy2);
|
||||
}
|
||||
|
||||
|
||||
public void testPropertyOverride()
|
||||
{
|
||||
// Register Policy
|
||||
PropertyPolicyDelegate<TestPropertyPolicy> delegate = policyComponent.registerPropertyPolicy(TestPropertyPolicy.class);
|
||||
|
||||
// Bind Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour baseBehaviour = new JavaBehaviour(this, "baseTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, BASE_TYPE, BASE_PROP_A, baseBehaviour);
|
||||
Behaviour folderBehaviour = new JavaBehaviour(this, "folderTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FOLDER_TYPE, BASE_PROP_A, folderBehaviour);
|
||||
Behaviour folderBehaviourD = new JavaBehaviour(this, "folderTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FOLDER_TYPE, FOLDER_PROP_D, folderBehaviourD);
|
||||
|
||||
// Invoke Policies
|
||||
TestPropertyPolicy basePolicy = delegate.get(BASE_TYPE, BASE_PROP_A);
|
||||
String baseResult = basePolicy.test("base");
|
||||
assertEquals("Base: base", baseResult);
|
||||
TestPropertyPolicy filePolicy = delegate.get(FILE_TYPE, BASE_PROP_A);
|
||||
String fileResult = filePolicy.test("file");
|
||||
assertEquals("Base: file", fileResult);
|
||||
TestPropertyPolicy folderPolicy = delegate.get(FOLDER_TYPE, BASE_PROP_A);
|
||||
String folderResult = folderPolicy.test("folder");
|
||||
assertEquals("Folder: folder", folderResult);
|
||||
TestPropertyPolicy folderPolicy2 = delegate.get(FOLDER_TYPE, FOLDER_PROP_D);
|
||||
String folderResult2 = folderPolicy2.test("folder");
|
||||
assertEquals("Folder: folder", folderResult2);
|
||||
}
|
||||
|
||||
|
||||
public void testPropertyWildcard()
|
||||
{
|
||||
// Register Policy
|
||||
PropertyPolicyDelegate<TestPropertyPolicy> delegate = policyComponent.registerPropertyPolicy(TestPropertyPolicy.class);
|
||||
|
||||
// Bind Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour baseBehaviour = new JavaBehaviour(this, "baseTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, BASE_TYPE, baseBehaviour);
|
||||
Behaviour folderBehaviour = new JavaBehaviour(this, "folderTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FOLDER_TYPE, folderBehaviour);
|
||||
Behaviour aspectBehaviour = new JavaBehaviour(this, "aspectTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, TEST_ASPECT, aspectBehaviour);
|
||||
|
||||
// Invoke Policies
|
||||
TestPropertyPolicy basePolicy = delegate.get(BASE_TYPE, BASE_PROP_A);
|
||||
String baseResult = basePolicy.test("base");
|
||||
assertEquals("Base: base", baseResult);
|
||||
TestPropertyPolicy filePolicy = delegate.get(FILE_TYPE, BASE_PROP_A);
|
||||
String fileResult = filePolicy.test("file");
|
||||
assertEquals("Base: file", fileResult);
|
||||
TestPropertyPolicy folderPolicy = delegate.get(FOLDER_TYPE, BASE_PROP_A);
|
||||
String folderResult = folderPolicy.test("folder");
|
||||
assertEquals("Folder: folder", folderResult);
|
||||
TestPropertyPolicy folderPolicy2 = delegate.get(FOLDER_TYPE, FOLDER_PROP_D);
|
||||
String folderResult2 = folderPolicy2.test("folder");
|
||||
assertEquals("Folder: folder", folderResult2);
|
||||
TestPropertyPolicy aspectPolicy = delegate.get(TEST_ASPECT, ASPECT_PROP_A);
|
||||
String aspectResult = aspectPolicy.test("aspect_prop_a");
|
||||
assertEquals("Aspect: aspect_prop_a", aspectResult);
|
||||
TestPropertyPolicy aspectPolicy2 = delegate.get(TEST_ASPECT, FOLDER_PROP_D);
|
||||
String aspectResult2 = aspectPolicy2.test("aspect_folder_d");
|
||||
assertEquals("Aspect: aspect_folder_d", aspectResult2);
|
||||
|
||||
// Override wild-card with specific property binding
|
||||
Behaviour folderDBehaviour = new JavaBehaviour(this, "folderDTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FOLDER_TYPE, FOLDER_PROP_D, folderDBehaviour);
|
||||
TestPropertyPolicy folderPolicy3 = delegate.get(FOLDER_TYPE, FOLDER_PROP_D);
|
||||
String folderResult3 = folderPolicy3.test("folder");
|
||||
assertEquals("FolderD: folder", folderResult3);
|
||||
}
|
||||
|
||||
|
||||
public void testPropertyCache()
|
||||
{
|
||||
// Register Policy
|
||||
PropertyPolicyDelegate<TestPropertyPolicy> delegate = policyComponent.registerPropertyPolicy(TestPropertyPolicy.class);
|
||||
|
||||
// Bind Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour baseBehaviour = new JavaBehaviour(this, "baseTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, BASE_TYPE, baseBehaviour);
|
||||
Behaviour folderBehaviour = new JavaBehaviour(this, "folderTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FOLDER_TYPE, folderBehaviour);
|
||||
Behaviour folderDBehaviour = new JavaBehaviour(this, "folderDTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, FOLDER_TYPE, FOLDER_PROP_D, folderDBehaviour);
|
||||
Behaviour aspectBehaviour = new JavaBehaviour(this, "aspectTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, TEST_ASPECT, aspectBehaviour);
|
||||
|
||||
// Invoke Policies
|
||||
TestPropertyPolicy filePolicy = delegate.get(FILE_TYPE, BASE_PROP_A);
|
||||
String fileResult = filePolicy.test("file");
|
||||
assertEquals("Base: file", fileResult);
|
||||
TestPropertyPolicy folderPolicy = delegate.get(FOLDER_TYPE, FOLDER_PROP_D);
|
||||
String folderResult = folderPolicy.test("folder");
|
||||
assertEquals("FolderD: folder", folderResult);
|
||||
|
||||
// Re-bind Behaviour
|
||||
Behaviour newBaseBehaviour = new JavaBehaviour(this, "newBaseTest");
|
||||
policyComponent.bindPropertyBehaviour(policyName, BASE_TYPE, newBaseBehaviour);
|
||||
|
||||
// Re-invoke Policies
|
||||
TestPropertyPolicy filePolicy2 = delegate.get(FILE_TYPE, BASE_PROP_A);
|
||||
String fileResult2 = filePolicy2.test("file");
|
||||
assertEquals("NewBase: file", fileResult2);
|
||||
TestPropertyPolicy folderPolicy2 = delegate.get(FOLDER_TYPE, FOLDER_PROP_D);
|
||||
String folderResult2 = folderPolicy2.test("folder");
|
||||
assertEquals("FolderD: folder", folderResult2);
|
||||
}
|
||||
|
||||
|
||||
public void testAssociationDelegate()
|
||||
{
|
||||
// Register Policy
|
||||
AssociationPolicyDelegate<TestAssociationPolicy> delegate = policyComponent.registerAssociationPolicy(TestAssociationPolicy.class);
|
||||
|
||||
// Bind Association Behaviour
|
||||
QName policyName = QName.createQName(TEST_NAMESPACE, "test");
|
||||
Behaviour baseBehaviour = new JavaBehaviour(this, "baseTest");
|
||||
policyComponent.bindAssociationBehaviour(policyName, BASE_TYPE, BASE_ASSOC_A, baseBehaviour);
|
||||
|
||||
// Test single Policy delegate
|
||||
Collection<TestAssociationPolicy> filePolicies = delegate.getList(FILE_TYPE, BASE_ASSOC_A);
|
||||
assertNotNull(filePolicies);
|
||||
assertTrue(filePolicies.size() == 1);
|
||||
TestAssociationPolicy filePolicy = delegate.get(FILE_TYPE, BASE_ASSOC_A);
|
||||
assertNotNull(filePolicy);
|
||||
String fileResult = filePolicy.test("file");
|
||||
assertEquals("Base: file", fileResult);
|
||||
|
||||
// Bind Service Behaviour
|
||||
Behaviour serviceBehaviour = new JavaBehaviour(this, "serviceTest");
|
||||
policyComponent.bindAssociationBehaviour(policyName, this, serviceBehaviour);
|
||||
|
||||
// Test multi Policy delegate
|
||||
Collection<TestAssociationPolicy> file2Policies = delegate.getList(FILE_TYPE, BASE_ASSOC_A);
|
||||
assertNotNull(file2Policies);
|
||||
assertTrue(file2Policies.size() == 2);
|
||||
TestAssociationPolicy filePolicy2 = delegate.get(FILE_TYPE, BASE_ASSOC_A);
|
||||
assertNotNull(filePolicy2);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// The following interfaces represents policies
|
||||
//
|
||||
|
||||
public interface TestClassPolicy extends ClassPolicy
|
||||
{
|
||||
static String NAMESPACE = TEST_NAMESPACE;
|
||||
public String test(String argument);
|
||||
}
|
||||
|
||||
public interface TestPropertyPolicy extends PropertyPolicy
|
||||
{
|
||||
static String NAMESPACE = TEST_NAMESPACE;
|
||||
public String test(String argument);
|
||||
}
|
||||
|
||||
public interface TestAssociationPolicy extends AssociationPolicy
|
||||
{
|
||||
static String NAMESPACE = TEST_NAMESPACE;
|
||||
public String test(String argument);
|
||||
}
|
||||
|
||||
public interface InvalidMetaDataPolicy extends ClassPolicy
|
||||
{
|
||||
static int NAMESPACE = 0;
|
||||
public String test(String nodeRef);
|
||||
}
|
||||
|
||||
public interface NoMethodPolicy extends ClassPolicy
|
||||
{
|
||||
}
|
||||
|
||||
public interface MultiMethodPolicy extends ClassPolicy
|
||||
{
|
||||
public void a();
|
||||
public void b();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// The following methods represent Java Behaviours
|
||||
//
|
||||
|
||||
public String validTest(String argument)
|
||||
{
|
||||
return "ValidTest: " + argument;
|
||||
}
|
||||
|
||||
public String baseTest(String argument)
|
||||
{
|
||||
return "Base: " + argument;
|
||||
}
|
||||
|
||||
public String newBaseTest(String argument)
|
||||
{
|
||||
return "NewBase: " + argument;
|
||||
}
|
||||
|
||||
public String fileTest(String argument)
|
||||
{
|
||||
return "File: " + argument;
|
||||
}
|
||||
|
||||
public String folderTest(String argument)
|
||||
{
|
||||
return "Folder: " + argument;
|
||||
}
|
||||
|
||||
public String aspectTest(String argument)
|
||||
{
|
||||
return "Aspect: " + argument;
|
||||
}
|
||||
|
||||
public String folderDTest(String argument)
|
||||
{
|
||||
return "FolderD: " + argument;
|
||||
}
|
||||
|
||||
public String serviceTest(String argument)
|
||||
{
|
||||
return "Service: " + argument;
|
||||
}
|
||||
|
||||
}
|
52
source/java/org/alfresco/repo/policy/PolicyDefinition.java
Normal file
52
source/java/org/alfresco/repo/policy/PolicyDefinition.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a Policy
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
*/
|
||||
public interface PolicyDefinition<P extends Policy>
|
||||
{
|
||||
/**
|
||||
* Gets the name of the Policy
|
||||
*
|
||||
* @return policy name
|
||||
*/
|
||||
public QName getName();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Policy interface class
|
||||
*
|
||||
* @return the class
|
||||
*/
|
||||
public Class<P> getPolicyInterface();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Policy type
|
||||
* @return the policy type
|
||||
*/
|
||||
public PolicyType getType();
|
||||
}
|
39
source/java/org/alfresco/repo/policy/PolicyException.java
Normal file
39
source/java/org/alfresco/repo/policy/PolicyException.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* Base Policy Exception.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public class PolicyException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 3761122726173290550L;
|
||||
|
||||
|
||||
public PolicyException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public PolicyException(String msg, Throwable cause)
|
||||
{
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
234
source/java/org/alfresco/repo/policy/PolicyFactory.java
Normal file
234
source/java/org/alfresco/repo/policy/PolicyFactory.java
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* A Policy Factory is responsible for creating Policy implementations.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <B> the type of binding
|
||||
* @param <P> the policy interface
|
||||
*/
|
||||
/*package*/ class PolicyFactory<B extends BehaviourBinding, P extends Policy>
|
||||
{
|
||||
// Behaviour Index to query
|
||||
private BehaviourIndex<B> index;
|
||||
|
||||
// The policy interface class
|
||||
private Class<P> policyClass;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param policyClass the policy class
|
||||
* @param index the behaviour index to query
|
||||
*/
|
||||
/*package*/ PolicyFactory(Class<P> policyClass, BehaviourIndex<B> index)
|
||||
{
|
||||
this.policyClass = policyClass;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Policy class created by this factory
|
||||
*
|
||||
* @return the policy class
|
||||
*/
|
||||
protected Class<P> getPolicyClass()
|
||||
{
|
||||
return policyClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a Policy implementation for the specified binding
|
||||
*
|
||||
* @param binding the binding
|
||||
* @return the policy implementation
|
||||
*/
|
||||
public P create(B binding)
|
||||
{
|
||||
Collection<P> policyInterfaces = createList(binding);
|
||||
return toPolicy(policyInterfaces);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a collection of Policy implementations for the specified binding
|
||||
*
|
||||
* @param binding the binding
|
||||
* @return the collection of policy implementations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<P> createList(B binding)
|
||||
{
|
||||
Collection<BehaviourDefinition> behaviourDefs = index.find(binding);
|
||||
List<P> policyInterfaces = new ArrayList<P>(behaviourDefs.size());
|
||||
for (BehaviourDefinition behaviourDef : behaviourDefs)
|
||||
{
|
||||
Behaviour behaviour = behaviourDef.getBehaviour();
|
||||
P policyIF = behaviour.getInterface(policyClass);
|
||||
policyInterfaces.add(policyIF);
|
||||
}
|
||||
|
||||
return policyInterfaces;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a single aggregate policy implementation for the specified
|
||||
* collection of policy implementations.
|
||||
*
|
||||
* @param policyList the policy implementations to aggregate
|
||||
* @return the aggregate policy implementation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public P toPolicy(Collection<P> policyList)
|
||||
{
|
||||
if (policyList.size() == 1)
|
||||
{
|
||||
return policyList.iterator().next();
|
||||
}
|
||||
else if (policyList.size() == 0)
|
||||
{
|
||||
return (P)Proxy.newProxyInstance(policyClass.getClassLoader(),
|
||||
new Class[]{policyClass}, new NOOPHandler());
|
||||
}
|
||||
else
|
||||
{
|
||||
return (P)Proxy.newProxyInstance(policyClass.getClassLoader(),
|
||||
new Class[]{policyClass, PolicyList.class}, new MultiHandler<P>(policyList));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NOOP Invocation Handler.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
private static class NOOPHandler implements InvocationHandler
|
||||
{
|
||||
/* (non-Javadoc)
|
||||
* @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
|
||||
{
|
||||
if (method.getName().equals("toString"))
|
||||
{
|
||||
return toString();
|
||||
}
|
||||
else if (method.getName().equals("hashCode"))
|
||||
{
|
||||
return hashCode();
|
||||
}
|
||||
else if (method.getName().equals("equals"))
|
||||
{
|
||||
return equals(args[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multi-policy Invocation Handler.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <P> policy interface
|
||||
*/
|
||||
@SuppressWarnings("hiding")
|
||||
private static class MultiHandler<P> implements InvocationHandler, PolicyList
|
||||
{
|
||||
private Collection<P> policyInterfaces;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param policyInterfaces the collection of policy implementations
|
||||
*/
|
||||
public MultiHandler(Collection<P> policyInterfaces)
|
||||
{
|
||||
this.policyInterfaces = Collections.unmodifiableCollection(policyInterfaces);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @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 PolicyList level methods
|
||||
if (method.getDeclaringClass().equals(PolicyList.class))
|
||||
{
|
||||
return method.invoke(this, args);
|
||||
}
|
||||
|
||||
// Handle Object level methods
|
||||
if (method.getName().equals("toString"))
|
||||
{
|
||||
return toString() + ": wrapped " + policyInterfaces.size() + " policies";
|
||||
}
|
||||
else if (method.getName().equals("hashCode"))
|
||||
{
|
||||
return hashCode();
|
||||
}
|
||||
else if (method.getName().equals("equals"))
|
||||
{
|
||||
return equals(args[0]);
|
||||
}
|
||||
|
||||
// Invoke each wrapped policy in turn
|
||||
try
|
||||
{
|
||||
Object result = null;
|
||||
for (P policyInterface : policyInterfaces)
|
||||
{
|
||||
result = method.invoke(policyInterface, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (InvocationTargetException e)
|
||||
{
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.PolicyList#getPolicies()
|
||||
*/
|
||||
public Collection getPolicies()
|
||||
{
|
||||
return policyInterfaces;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
source/java/org/alfresco/repo/policy/PolicyList.java
Normal file
30
source/java/org/alfresco/repo/policy/PolicyList.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author David Caruana
|
||||
*/
|
||||
/*package*/ interface PolicyList<P extends Policy>
|
||||
{
|
||||
/**
|
||||
* @return the set of policies within this policy set
|
||||
*/
|
||||
public Collection<P> getPolicies();
|
||||
}
|
449
source/java/org/alfresco/repo/policy/PolicyScope.java
Normal file
449
source/java/org/alfresco/repo/policy/PolicyScope.java
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Policy scope.
|
||||
* <p>
|
||||
* Helper often used by policies which require information
|
||||
* about a node to be gathered, for example onCopy or onCreateVersion.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
public class PolicyScope extends AspectDetails
|
||||
{
|
||||
/**
|
||||
* The aspects
|
||||
*/
|
||||
protected Map<QName, AspectDetails> aspectCopyDetails = new HashMap<QName, AspectDetails>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param classRef the class reference
|
||||
*/
|
||||
public PolicyScope(QName classRef)
|
||||
{
|
||||
super(classRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property
|
||||
*
|
||||
* @param classRef the class reference
|
||||
* @param qName the qualified name of the property
|
||||
* @param value the value of the property
|
||||
*/
|
||||
public void addProperty(QName classRef, QName qName, Serializable value)
|
||||
{
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
addProperty(qName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails == null)
|
||||
{
|
||||
// Add the aspect
|
||||
aspectDetails = addAspect(classRef);
|
||||
}
|
||||
aspectDetails.addProperty(qName, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a property from the list
|
||||
*
|
||||
* @param classRef the class reference
|
||||
* @param qName the qualified name
|
||||
*/
|
||||
public void removeProperty(QName classRef, QName qName)
|
||||
{
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
removeProperty(qName);
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails != null)
|
||||
{
|
||||
aspectDetails.removeProperty(qName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the properties
|
||||
*
|
||||
* @param classRef the class ref
|
||||
* @return the properties that should be copied
|
||||
*/
|
||||
public Map<QName, Serializable> getProperties(QName classRef)
|
||||
{
|
||||
Map<QName, Serializable> result = null;
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
result = getProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails != null)
|
||||
{
|
||||
result = aspectDetails.getProperties();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child association
|
||||
*
|
||||
* @param classRef
|
||||
* @param qname
|
||||
* @param childAssocRef
|
||||
*/
|
||||
public void addChildAssociation(QName classRef, ChildAssociationRef childAssocRef)
|
||||
{
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
addChildAssociation(childAssocRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails == null)
|
||||
{
|
||||
// Add the aspect
|
||||
aspectDetails = addAspect(classRef);
|
||||
}
|
||||
aspectDetails.addChildAssociation(childAssocRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param classRef
|
||||
* @param childAssocRef
|
||||
* @param alwaysTraverseAssociation
|
||||
*/
|
||||
public void addChildAssociation(QName classRef, ChildAssociationRef childAssocRef, boolean alwaysTraverseAssociation)
|
||||
{
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
addChildAssociation(childAssocRef, alwaysTraverseAssociation);
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails == null)
|
||||
{
|
||||
// Add the aspect
|
||||
aspectDetails = addAspect(classRef);
|
||||
}
|
||||
aspectDetails.addChildAssociation(childAssocRef, alwaysTraverseAssociation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a child association
|
||||
*
|
||||
* @param classRef
|
||||
* @return
|
||||
*/
|
||||
public List<ChildAssociationRef> getChildAssociations(QName classRef)
|
||||
{
|
||||
List<ChildAssociationRef> result = null;
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
result = getChildAssociations();
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails != null)
|
||||
{
|
||||
result = aspectDetails.getChildAssociations();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isChildAssociationRefAlwaysTraversed(QName classRef, ChildAssociationRef childAssocRef)
|
||||
{
|
||||
boolean result = false;
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
result = isChildAssociationRefAlwaysTraversed(childAssocRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails != null)
|
||||
{
|
||||
result = aspectDetails.isChildAssociationRefAlwaysTraversed(childAssocRef);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an association
|
||||
*
|
||||
* @param classRef
|
||||
* @param qname
|
||||
* @param nodeAssocRef
|
||||
*/
|
||||
public void addAssociation(QName classRef, AssociationRef nodeAssocRef)
|
||||
{
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
addAssociation(nodeAssocRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails == null)
|
||||
{
|
||||
// Add the aspect
|
||||
aspectDetails = addAspect(classRef);
|
||||
}
|
||||
aspectDetails.addAssociation(nodeAssocRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get associations
|
||||
*
|
||||
* @param classRef
|
||||
* @return
|
||||
*/
|
||||
public List<AssociationRef> getAssociations(QName classRef)
|
||||
{
|
||||
List<AssociationRef> result = null;
|
||||
if (classRef.equals(this.classRef) == true)
|
||||
{
|
||||
result = getAssociations();
|
||||
}
|
||||
else
|
||||
{
|
||||
AspectDetails aspectDetails = this.aspectCopyDetails.get(classRef);
|
||||
if (aspectDetails != null)
|
||||
{
|
||||
result = aspectDetails.getAssociations();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an aspect
|
||||
*
|
||||
* @param aspect the aspect class reference
|
||||
* @return the apsect copy details (returned as a helper)
|
||||
*/
|
||||
public AspectDetails addAspect(QName aspect)
|
||||
{
|
||||
AspectDetails result = new AspectDetails(aspect);
|
||||
this.aspectCopyDetails.put(aspect, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an aspect from the list
|
||||
*
|
||||
* @param aspect the aspect class reference
|
||||
*/
|
||||
public void removeAspect(QName aspect)
|
||||
{
|
||||
this.aspectCopyDetails.remove(aspect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the aspects
|
||||
*
|
||||
* @return a list of aspect to copy
|
||||
*/
|
||||
public Set<QName> getAspects()
|
||||
{
|
||||
return this.aspectCopyDetails.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aspect details class.
|
||||
* <p>
|
||||
* Contains the details of an aspect this can be used for copying or versioning.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
/*package*/ class AspectDetails
|
||||
{
|
||||
/**
|
||||
* The properties
|
||||
*/
|
||||
protected Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
||||
|
||||
/**
|
||||
* The child associations
|
||||
*/
|
||||
protected List<ChildAssociationRef> childAssocs = new ArrayList<ChildAssociationRef>();
|
||||
|
||||
/**
|
||||
* The target associations
|
||||
*/
|
||||
protected List<AssociationRef> targetAssocs = new ArrayList<AssociationRef>();
|
||||
|
||||
/**
|
||||
* The class ref of the aspect
|
||||
*/
|
||||
protected QName classRef;
|
||||
|
||||
/**
|
||||
* Map of assocs that will always be traversed
|
||||
*/
|
||||
protected Map<ChildAssociationRef, ChildAssociationRef> alwaysTraverseMap = new HashMap<ChildAssociationRef, ChildAssociationRef>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param classRef the class ref
|
||||
*/
|
||||
public AspectDetails(QName classRef)
|
||||
{
|
||||
this.classRef = classRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property to the list
|
||||
*
|
||||
* @param qName the qualified name of the property
|
||||
* @param value the value of the property
|
||||
*/
|
||||
public void addProperty(QName qName, Serializable value)
|
||||
{
|
||||
this.properties.put(qName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a property from the list
|
||||
*
|
||||
* @param qName the qualified name of the property
|
||||
*/
|
||||
public void removeProperty(QName qName)
|
||||
{
|
||||
this.properties.remove(qName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map of properties
|
||||
*
|
||||
* @return map of property names and values
|
||||
*/
|
||||
public Map<QName, Serializable> getProperties()
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child association
|
||||
*
|
||||
* @param childAssocRef the child association reference
|
||||
*/
|
||||
protected void addChildAssociation(ChildAssociationRef childAssocRef)
|
||||
{
|
||||
this.childAssocs.add(childAssocRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child association
|
||||
*
|
||||
* @param childAssocRef the child assoc reference
|
||||
* @param alwaysDeepCopy indicates whether the assoc should always be traversed
|
||||
*/
|
||||
protected void addChildAssociation(ChildAssociationRef childAssocRef, boolean alwaysTraverseAssociation)
|
||||
{
|
||||
addChildAssociation(childAssocRef);
|
||||
|
||||
if (alwaysTraverseAssociation == true)
|
||||
{
|
||||
// Add to the list of deep copy child associations
|
||||
this.alwaysTraverseMap.put(childAssocRef, childAssocRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a child association ref is always traversed or not
|
||||
*
|
||||
* @param childAssocRef the child association reference
|
||||
* @return true if the assoc is always traversed, false otherwise
|
||||
*/
|
||||
protected boolean isChildAssociationRefAlwaysTraversed(ChildAssociationRef childAssocRef)
|
||||
{
|
||||
return this.alwaysTraverseMap.containsKey(childAssocRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the child associations to be copied
|
||||
*
|
||||
* @return map containing the child associations to be copied
|
||||
*/
|
||||
public List<ChildAssociationRef> getChildAssociations()
|
||||
{
|
||||
return this.childAssocs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an association to be copied
|
||||
*
|
||||
* @param qname the qualified name of the association
|
||||
* @param nodeAssocRef the association reference
|
||||
*/
|
||||
protected void addAssociation(AssociationRef nodeAssocRef)
|
||||
{
|
||||
this.targetAssocs.add(nodeAssocRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map of associations to be copied
|
||||
*
|
||||
* @return a map conatining the associations to be copied
|
||||
*/
|
||||
public List<AssociationRef> getAssociations()
|
||||
{
|
||||
return this.targetAssocs;
|
||||
}
|
||||
}
|
31
source/java/org/alfresco/repo/policy/PolicyType.java
Normal file
31
source/java/org/alfresco/repo/policy/PolicyType.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* Type of Policy.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public enum PolicyType
|
||||
{
|
||||
Class,
|
||||
Property,
|
||||
Association
|
||||
};
|
26
source/java/org/alfresco/repo/policy/PropertyPolicy.java
Normal file
26
source/java/org/alfresco/repo/policy/PropertyPolicy.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
/**
|
||||
* Marker interface for representing a Property-level Policy.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public interface PropertyPolicy extends Policy
|
||||
{
|
||||
}
|
208
source/java/org/alfresco/repo/policy/PropertyPolicyDelegate.java
Normal file
208
source/java/org/alfresco/repo/policy/PropertyPolicyDelegate.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Delegate for a Class Feature-level (Property and Association) Policies. Provides
|
||||
* access to Policy Interface implementations which invoke the appropriate bound behaviours.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
* @param <P> the policy interface
|
||||
*/
|
||||
public class PropertyPolicyDelegate<P extends PropertyPolicy>
|
||||
{
|
||||
private DictionaryService dictionary;
|
||||
private CachedPolicyFactory<ClassFeatureBehaviourBinding, P> factory;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param dictionary the dictionary service
|
||||
* @param policyClass the policy interface class
|
||||
* @param index the behaviour index to query against
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
/*package*/ PropertyPolicyDelegate(DictionaryService dictionary, Class<P> policyClass, BehaviourIndex<ClassFeatureBehaviourBinding> index)
|
||||
{
|
||||
// Get list of all pre-registered behaviours for the policy and
|
||||
// ensure they are valid.
|
||||
Collection<BehaviourDefinition> definitions = index.getAll();
|
||||
for (BehaviourDefinition definition : definitions)
|
||||
{
|
||||
definition.getBehaviour().getInterface(policyClass);
|
||||
}
|
||||
|
||||
// Rely on cached implementation of policy factory
|
||||
// Note: Could also use PolicyFactory (without caching)
|
||||
this.factory = new CachedPolicyFactory<ClassFeatureBehaviourBinding, P>(policyClass, index);
|
||||
this.dictionary = dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the validity of the given property type
|
||||
*
|
||||
* @param assocTypeQName
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void checkPropertyType(QName propertyQName) throws IllegalArgumentException
|
||||
{
|
||||
PropertyDefinition propertyDef = dictionary.getProperty(propertyQName);
|
||||
if (propertyDef == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Property " + propertyQName + " has not been defined in the data dictionary");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Policy implementation for the specified Class and Propery
|
||||
*
|
||||
* When multiple behaviours are bound to the policy for the class feature, an
|
||||
* aggregate policy implementation is returned which invokes each policy
|
||||
* in turn.
|
||||
*
|
||||
* @param classQName the class qualified name
|
||||
* @param propertyQName the property qualified name
|
||||
* @return the policy
|
||||
*/
|
||||
public P get(QName classQName, QName propertyQName)
|
||||
{
|
||||
return get(null, classQName, propertyQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Policy implementation for the specified Class and Propery
|
||||
*
|
||||
* When multiple behaviours are bound to the policy for the class feature, an
|
||||
* aggregate policy implementation is returned which invokes each policy
|
||||
* in turn.
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the class qualified name
|
||||
* @param propertyQName the property qualified name
|
||||
* @return the policy
|
||||
*/
|
||||
public P get(NodeRef nodeRef, QName classQName, QName propertyQName)
|
||||
{
|
||||
checkPropertyType(propertyQName);
|
||||
return factory.create(new ClassFeatureBehaviourBinding(dictionary, nodeRef, classQName, propertyQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of Policy implementations for the specified Class and Property
|
||||
*
|
||||
* @param classQName the class qualified name
|
||||
* @param propertyQName the property qualified name
|
||||
* @return the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(QName classQName, QName propertyQName)
|
||||
{
|
||||
return getList(null, classQName, propertyQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of Policy implementations for the specified Class and Property
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQName the class qualified name
|
||||
* @param propertyQName the property qualified name
|
||||
* @return the collection of policies
|
||||
*/
|
||||
public Collection<P> getList(NodeRef nodeRef, QName classQName, QName propertyQName)
|
||||
{
|
||||
checkPropertyType(propertyQName);
|
||||
return factory.createList(new ClassFeatureBehaviourBinding(dictionary, nodeRef, classQName, propertyQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <tt>Policy</tt> for all the given Class and Property
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @param propertyQName the property qualified name
|
||||
* @return Return the policy
|
||||
*/
|
||||
public P get(Set<QName> classQNames, QName propertyQName)
|
||||
{
|
||||
return get(null, classQNames, propertyQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <tt>Policy</tt> for all the given Class and Property
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQNames the class qualified names
|
||||
* @param propertyQName the property qualified name
|
||||
* @return Return the policy
|
||||
*/
|
||||
public P get(NodeRef nodeRef, Set<QName> classQNames, QName propertyQName)
|
||||
{
|
||||
checkPropertyType(propertyQName);
|
||||
return factory.toPolicy(getList(nodeRef, classQNames, propertyQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the <tt>Policy</tt> instances for all the given Classes and Properties
|
||||
*
|
||||
* @param classQNames the class qualified names
|
||||
* @param propertyQName the property qualified name
|
||||
* @return Return the policies
|
||||
*/
|
||||
public Collection<P> getList(Set<QName> classQNames, QName propertyQName)
|
||||
{
|
||||
return getList(null, classQNames, propertyQName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the <tt>Policy</tt> instances for all the given Classes and Properties
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param classQNames the class qualified names
|
||||
* @param propertyQName the property qualified name
|
||||
* @return Return the policies
|
||||
*/
|
||||
public Collection<P> getList(NodeRef nodeRef, Set<QName> classQNames, QName propertyQName)
|
||||
{
|
||||
checkPropertyType(propertyQName);
|
||||
Collection<P> policies = new HashSet<P>();
|
||||
for (QName classQName : classQNames)
|
||||
{
|
||||
P policy = factory.create(new ClassFeatureBehaviourBinding(dictionary, nodeRef, classQName, propertyQName));
|
||||
if (policy instanceof PolicyList)
|
||||
{
|
||||
policies.addAll(((PolicyList<P>)policy).getPolicies());
|
||||
}
|
||||
else
|
||||
{
|
||||
policies.add(policy);
|
||||
}
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.policy;
|
||||
|
||||
|
||||
/**
|
||||
* Behaviour binding to a Service.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class ServiceBehaviourBinding implements BehaviourBinding
|
||||
{
|
||||
// The service
|
||||
private Object service;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param service the service
|
||||
*/
|
||||
/*package*/ ServiceBehaviourBinding(Object service)
|
||||
{
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourBinding#generaliseBinding()
|
||||
*/
|
||||
public BehaviourBinding generaliseBinding()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Service
|
||||
*
|
||||
* @return the service
|
||||
*/
|
||||
public Object getService()
|
||||
{
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null || !(obj instanceof ServiceBehaviourBinding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return service.equals(((ServiceBehaviourBinding)obj).service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return service.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ServiceBinding[service=" + this + "]";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
<model name="test:policycomponenttest" xmlns="http://www.alfresco.org/model/dictionary/1.0">
|
||||
|
||||
<imports>
|
||||
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
|
||||
</imports>
|
||||
|
||||
<namespaces>
|
||||
<namespace uri="http://www.alfresco.org/test/policycomponenttest/1.0" prefix="test"/>
|
||||
</namespaces>
|
||||
|
||||
<types>
|
||||
|
||||
<type name="test:base">
|
||||
<properties>
|
||||
<property name="test:base_a">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
</properties>
|
||||
<associations>
|
||||
<association name="test:base_assoc_a">
|
||||
<target>
|
||||
<class>test:base</class>
|
||||
</target>
|
||||
</association>
|
||||
</associations>
|
||||
</type>
|
||||
|
||||
<type name="test:file">
|
||||
<parent>test:base</parent>
|
||||
<properties>
|
||||
<property name="test:file_b">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
<property name="test:file_c">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
</properties>
|
||||
<overrides>
|
||||
<property name="test:base_a">
|
||||
<default>an overriden default value</default>
|
||||
</property>
|
||||
</overrides>
|
||||
</type>
|
||||
|
||||
<type name="test:folder">
|
||||
<parent>test:base</parent>
|
||||
<properties>
|
||||
<property name="test:folder_d">
|
||||
<type>d:text</type>
|
||||
</property>
|
||||
</properties>
|
||||
<associations>
|
||||
<association name="test:folder_assoc_d">
|
||||
<target>
|
||||
<class>test:folder</class>
|
||||
</target>
|
||||
</association>
|
||||
</associations>
|
||||
</type>
|
||||
|
||||
</types>
|
||||
|
||||
<aspects>
|
||||
<aspect name="test:aspect">
|
||||
<properties>
|
||||
<property name="test:aspect_a">
|
||||
<type>d:int</type>
|
||||
</property>
|
||||
</properties>
|
||||
</aspect>
|
||||
</aspects>
|
||||
|
||||
</model>
|
Reference in New Issue
Block a user