All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dynamicextensionsalfresco.policy.AnnotationBasedBehaviourRegistrar Maven / Gradle / Ivy

Go to download

Adds an OSGi container to alfresco repository supporting dynamic code reloading, classpath isolation and a bunch of other useful features

There is a newer version: 3.1.0
Show newest version
package com.github.dynamicextensionsalfresco.policy;

import com.github.dynamicextensionsalfresco.AbstractAnnotationBasedRegistrar;
import com.github.dynamicextensionsalfresco.behaviours.annotations.*;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.Policy;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.PolicyType;
import org.alfresco.service.namespace.QName;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Registers {@link Behaviour}-annotated classes from beans in a {@link BeanFactory}.
 * 
 * @author Laurens Fridael
 * 
 */
public class AnnotationBasedBehaviourRegistrar extends AbstractAnnotationBasedRegistrar {

	/* Dependencies */

	private PolicyComponent policyComponent;

	/* Operations */

	/**
	 * Binds Behaviours to beans in the {@link BeanFactory} based on Behaviour annotations.
	 */
	public void bindBehaviours() {
		for (final String beanName : getBeanFactory().getBeanDefinitionNames()) {
			final Behaviour behaviour = getBeanFactory().findAnnotationOnBean(beanName, Behaviour.class);
			if (behaviour != null) {
				bindBehaviours(getBeanFactory().getBean(beanName), behaviour);
			}
		}
	}

	protected void bindBehaviours(final Object bean, final Behaviour behaviour) {
		Assert.notNull(bean, "Bean cannot be null.");

		final QName[] classNames = parseQNames(behaviour.value(), behaviour);
		final NotificationFrequency notificationFrequency = behaviour.event().toNotificationFrequency();

		for (final Entry> entry : getBehaviourMethodsByType(bean.getClass())
				.entrySet()) {
			for (final BehaviourMethod behaviourMethod : entry.getValue()) {
				switch (entry.getKey()) {
				case Class:
					bindClassPolicyBehaviour(bean, behaviourMethod.method, behaviourMethod.policyName, classNames,
							notificationFrequency);
					break;
				case Association:
					bindAssocationPolicyBehaviour(bean, behaviourMethod.method, behaviourMethod.policyName, classNames,
							notificationFrequency);
					break;
				case Property:
					bindPropertyPolicyBehaviour(bean, behaviourMethod.method, behaviourMethod.policyName, classNames,
							notificationFrequency);
					break;
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	protected void bindClassPolicyBehaviour(final Object bean, final Method method, final QName policyName,
			QName[] classNames, NotificationFrequency notificationFrequency) {
		final ClassPolicy classPolicy = AnnotationUtils.findAnnotation(method, ClassPolicy.class);
		if (classPolicy != null) {
			classNames = parseQNames(classPolicy.value(), classPolicy);
			if (classPolicy.event().equals(Event.INHERITED_OR_ALL) == false) {
				notificationFrequency = classPolicy.event().toNotificationFrequency();
			}
		}
		final JavaBehaviour behaviour = new DescriptiveJavaBehaviour(bean, method, notificationFrequency);
		if (classNames.length > 0) {
			for (final QName className : classNames) {
				if (className != null) {
					if (logger.isDebugEnabled()) {
						logger.debug(
								"Binding class Behaviour for policy {} and class {} for method {} with notification frequency {}.",
                            policyName, className, method, notificationFrequency);
					}
					getPolicyComponent().bindClassBehaviour(policyName, className, behaviour);
				}
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Binding class Behaviour for policy {} for method {} with notification frequency {}.",
                    policyName, method, notificationFrequency);
			}
			getPolicyComponent().bindClassBehaviour(policyName, bean, behaviour);
		}
		warnAboutInapplicablePolicyAnnotations(method, AssociationPolicy.class, PropertyPolicy.class);
	}

	@SuppressWarnings("unchecked")
	protected void bindAssocationPolicyBehaviour(final Object bean, final Method method, final QName policyName,
			QName[] classNames, NotificationFrequency notificationFrequency) {
		final AssociationPolicy associationPolicy = AnnotationUtils.findAnnotation(method, AssociationPolicy.class);
		QName assocationName = null;
		if (associationPolicy != null) {
      final String[] localClassNames = associationPolicy.value();
      if (localClassNames.length > 0) {
        // fall back to @Behaviour classnames if none provided on @AssociationPolicy
        classNames = parseQNames(localClassNames, associationPolicy);
      }
			assocationName = parseQName(associationPolicy.association(), associationPolicy);
			if (associationPolicy.event().equals(Event.INHERITED_OR_ALL) == false) {
				notificationFrequency = associationPolicy.event().toNotificationFrequency();
			}
		}
		final JavaBehaviour behaviour = new DescriptiveJavaBehaviour(bean, method, notificationFrequency);
		if (classNames.length > 0) {
			for (final QName className : classNames) {
				if (assocationName != null) {
					if (logger.isDebugEnabled()) {
						logger.debug(
								"Binding association Behaviour for policy {}, class {} and association {} for method {} with notification frequency {}.",
                            policyName, className, assocationName, method, notificationFrequency);
					}
					getPolicyComponent().bindAssociationBehaviour(policyName, className, assocationName, behaviour);
				} else {
					if (logger.isDebugEnabled()) {
						logger.debug(
								"Binding association Behaviour for policy {} and class {} for method {} with notification frequency {}.",
                            policyName, className, method, notificationFrequency);
					}
					getPolicyComponent().bindAssociationBehaviour(policyName, className, behaviour);
				}
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug(
						"Binding association Behaviour for policy {} for method {} with notification frequency {}.",
                    policyName, method, notificationFrequency);
			}
			getPolicyComponent().bindAssociationBehaviour(policyName, bean, behaviour);
		}
		warnAboutInapplicablePolicyAnnotations(method, ClassPolicy.class, PropertyPolicy.class);
	}

	/*
	 * There don't seem to be any property policy interfaces defined in the Alfresco repository code. So binding
	 * property-level policies is left untested.
	 */
	@SuppressWarnings("unchecked")
	protected void bindPropertyPolicyBehaviour(final Object bean, final Method method, final QName policyName,
			QName[] classNames, NotificationFrequency notificationFrequency) {
		final PropertyPolicy propertyPolicy = AnnotationUtils.findAnnotation(method, PropertyPolicy.class);
		QName propertyName = null;
		if (propertyPolicy != null) {
			if (propertyPolicy.value().length > 0) {
				classNames = parseQNames(propertyPolicy.value(), propertyPolicy);
			}
			propertyName = parseQName(propertyPolicy.property(), propertyPolicy);
			if (propertyPolicy.event().equals(Event.INHERITED_OR_ALL) == false) {
				notificationFrequency = propertyPolicy.event().toNotificationFrequency();
			}
		}
		final JavaBehaviour behaviour = new DescriptiveJavaBehaviour(bean, method, notificationFrequency);
		if (classNames.length > 0) {
			for (final QName className : classNames) {
				if (propertyName != null) {
					if (logger.isDebugEnabled()) {
						logger.debug(
								"Binding property Behaviour for policy {}, class {} and property {} for method {} with notification frequency {}.",
                            policyName, className, propertyName, method, notificationFrequency);
					}
					getPolicyComponent().bindPropertyBehaviour(policyName, className, propertyName, behaviour);
				} else {
					if (logger.isDebugEnabled()) {
						logger.debug(
								"Binding property Behaviour for policy {} and class {} for method {} with notification frequency {}.",
                            policyName, className, method, notificationFrequency);
					}
					getPolicyComponent().bindPropertyBehaviour(policyName, className, behaviour);
				}
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Binding property Behaviour for policy {} for method {} with notification frequency {}.",
                    policyName, method, notificationFrequency);
			}
			getPolicyComponent().bindPropertyBehaviour(policyName, bean, behaviour);
		}

		warnAboutInapplicablePolicyAnnotations(method, ClassPolicy.class, AssociationPolicy.class);
	}

	/* Utility operations */

	private QName getPolicyQName(final Class policyClass) {
		QName qName = null;
		try {
			final Field field = policyClass.getField("QNAME");
			/* Field must be static, public and of type QName. */
			final int modifiers = field.getModifiers();
			if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)
					&& QName.class.isAssignableFrom(field.getType())) {
				qName = (QName) field.get(null);
			}
		} catch (final NoSuchFieldException e) {
			/* No need to handle this. Simply leave qName at null. */
		} catch (final SecurityException e) {
			throw new RuntimeException(e);
		} catch (final IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		return qName;
	}

	private void warnAboutInapplicablePolicyAnnotations(final Method method,
			final Class... annotationClasses) {
		for (final Class annotationClass : annotationClasses) {
			final Annotation annotation = AnnotationUtils.findAnnotation(method, annotationClass);
			if (annotation != null && logger.isWarnEnabled()) {
				logger.warn(
						"Found {} annotation on method {}. This annotation is not applicable here and will be ignored.",
						annotation.annotationType().getName(), method);
			}
		}
	}

	@SuppressWarnings("unchecked")
	private Map>> getPolicyInterfacesByType(final Class clazz) {
		final Map>> policyInterfacesByType = new HashMap>>();
		for (final Class interfaceClass : ClassUtils.getAllInterfacesForClassAsSet(clazz)) {
			PolicyType policyType = null;
			if (org.alfresco.repo.policy.ClassPolicy.class.isAssignableFrom(interfaceClass)) {
				policyType = PolicyType.Class;
			} else if (org.alfresco.repo.policy.AssociationPolicy.class.isAssignableFrom(interfaceClass)) {
				policyType = PolicyType.Association;
			} else if (org.alfresco.repo.policy.PropertyPolicy.class.isAssignableFrom(interfaceClass)) {
				policyType = PolicyType.Property;
			}
			if (policyType != null) {
				if (policyInterfacesByType.containsKey(policyType) == false) {
					policyInterfacesByType.put(policyType, new ArrayList>());
				}
				policyInterfacesByType.get(policyType).add((Class) interfaceClass);
			}
		}
		return policyInterfacesByType;
	}

	private Map> getBehaviourMethodsByType(final Class beanClass) {
		final Map>> policyInterfacesByType = getPolicyInterfacesByType(beanClass);
		final Map> behaviourMethodsByType = new HashMap>();
		for (final Entry>> entry : policyInterfacesByType.entrySet()) {
			final List behaviourMethods = new ArrayList();
			for (final Class policyInterface : entry.getValue()) {
				if (policyInterface.getMethods().length == 1) {
					final Method policyInterfaceMethod = policyInterface.getMethods()[0];
					final Method behaviourMethod = ReflectionUtils.findMethod(beanClass,
							policyInterfaceMethod.getName(), policyInterfaceMethod.getParameterTypes());
					if (behaviourMethod != null) {
						behaviourMethods.add(new BehaviourMethod(getPolicyQName(policyInterface), behaviourMethod));
					}
				}
			}
			if (behaviourMethods.isEmpty() == false) {
				behaviourMethodsByType.put(entry.getKey(), behaviourMethods);
			}
		}
		return behaviourMethodsByType;
	}

	/* Dependencies */

	public void setPolicyComponent(final PolicyComponent policyComponent) {
		this.policyComponent = policyComponent;
	}

	protected PolicyComponent getPolicyComponent() {
		return policyComponent;
	}

	/* Utility classes */

	private static class BehaviourMethod {

		final QName policyName;

		final Method method;

		private BehaviourMethod(final QName policyName, final Method method) {
			this.policyName = policyName;
			this.method = method;
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy