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:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit e1e6508fec
1095 changed files with 230566 additions and 0 deletions

View 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
{
}

View File

@@ -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;
}
}

View 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();
}

View 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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View 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();
}

View 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());
}
}

View 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();
}

View 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);
}
}

View 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();
}
}
}
}
}

View 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 + "]";
}
}

View 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();
}
}
}

View File

@@ -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 + "]";
}
}

View 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
{
}

View 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;
}
}

View 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() + "]";
}
}
}

View 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;
}

View 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);
}

View 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;
}
}
}

View 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;
}
}

View 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();
}

View 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);
}
}

View 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;
}
}
}

View 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();
}

View 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;
}
}

View 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
};

View 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
{
}

View 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;
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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>