Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ 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.repo.policy.Policy.Arg; import org.alfresco.repo.tenant.TenantService; 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.springframework.extensions.surf.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"; private static String ANNOTATION_ARG = "ARG_"; // 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, "*"); // Try lock timeout (MNT-11371) private long tryLockTimeout; public void setTryLockTimeout(long tryLockTimeout) { this.tryLockTimeout = tryLockTimeout; } /** * Construct * * @param dictionary dictionary service */ public PolicyComponentImpl(DictionaryService dictionary) { this.dictionary = dictionary; this.registeredPolicies = new HashMap<PolicyKey, PolicyDefinition>(); } /** * Sets the behaviour filter * * @param filter BehaviourFilter */ public void setBehaviourFilter(BehaviourFilter filter) { this.behaviourFilter = filter; } /** * Sets the tenant service * * @param tenantService TenantService */ public void setTenantService(TenantService tenantService) { PolicyFactory.setTenantService(tenantService); } /** * Sets the transaction-based policy invocation handler * * @param factory TransactionInvocationHandlerFactory */ public void setTransactionInvocationHandlerFactory(TransactionInvocationHandlerFactory factory) { PolicyFactory.setTransactionInvocationHandlerFactory(factory); } /* (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()), tryLockTimeout); if (logger.isInfoEnabled()) logger.info("Registered class policy " + definition.getName() + " (" + definition.getPolicyInterface() + ")"); return delegate; } @Override public void removeClassDefinition(BehaviourDefinition<ClassBehaviourBinding> definition) { getClassBehaviourIndex(definition.getPolicy()).removeClassBehaviour(definition); } /* (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()), tryLockTimeout); 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()), tryLockTimeout); 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); index.setTryLockTimeout(tryLockTimeout); 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); index.setTryLockTimeout(tryLockTimeout); 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); index.setTryLockTimeout(tryLockTimeout); 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(); // Extract Policy Arguments Class[] paramTypes = methods[0].getParameterTypes(); Arg[] args = new Arg[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { // Extract Policy Arg args[i] = (i == 0) ? Arg.KEY : Arg.START_VALUE; try { Field argMetadata = policyIF.getField(ANNOTATION_ARG + i); if (!Arg.class.isAssignableFrom(argMetadata.getType())) { throw new PolicyException("ARG_" + i + " metadata incorrectly specified in policy " + policyIF.getCanonicalName()); } args[i] = (Arg) argMetadata.get(null); if (i == 0 && (!args[i].equals(Arg.KEY))) { throw new PolicyException( "ARG_" + i + " specified in policy " + policyIF.getCanonicalName() + " must be a key"); } } catch (NoSuchFieldException e) { // Assume default ARG configuration } catch (IllegalAccessException e) { // Shouldn't get here (interface definitions must be accessible) } } // Create Policy Definition return new PolicyDefinitionImpl(QName.createQName(namespaceURI, name), policyIF, args); } /** * 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; private Arg[] args; /*package*/ PolicyDefinitionImpl(QName policy, Class policyIF, Arg[] args) { this.policy = policy; this.policyIF = policyIF; this.args = args; } /* (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; } } /* (non-Javadoc) * @see org.alfresco.repo.policy.PolicyDefinition#getArgument(int) */ public Arg getArgument(int index) { if (index < 0 || index > args.length - 1) { throw new IllegalArgumentException("Argument index " + index + " is invalid"); } return args[index]; } /* (non-Javadoc) * @see org.alfresco.repo.policy.PolicyDefinition#getArguments() */ public Arg[] getArguments() { return args; } } /** * 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; } } }