io.dyn.core.EventedBase.java Source code

Java tutorial

Introduction

Here is the source code for io.dyn.core.EventedBase.java

Source

/*
 * Copyright (c) 2011-2012 by the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 io.dyn.core;

import static io.dyn.el.SpelExpression.*;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import io.dyn.core.handler.AnnotationHandlerMethodResolver;
import io.dyn.core.handler.GuardedHandlerMethodInvoker;
import io.dyn.core.handler.Handler;
import io.dyn.core.handler.HandlerMethod;
import io.dyn.core.handler.HandlerMethodInvoker;
import io.dyn.core.handler.HandlerMethodResolver;
import io.dyn.core.handler.Handlers;
import io.dyn.core.handler.anno.On;
import io.dyn.el.SpelExpression;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.task.TaskExecutor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

/**
 * @author Jon Brisbin <jon@jbrisbin.com>
 */
public abstract class EventedBase<E extends EventedBase<? super E>>
        implements Evented<E>, Recyclable<E>, ApplicationContextAware, BeanFactoryAware, InitializingBean {

    private Map<String, Method> myMethods = new HashMap<>();

    {
        for (Method m : getClass().getDeclaredMethods()) {
            myMethods.put(m.getName(), m);
        }
    }

    protected ApplicationContext applicationContext;
    protected AutowireCapableBeanFactory beanFactory;
    @Autowired(required = false)
    protected ConfigurableConversionService conversionService;
    protected TaskExecutor executor = Tasks.currentExecutor();
    protected EvaluationContext evaluationContext;

    protected Map<String, Object> handlers = new HashMap<>();
    protected TreeMap<Expression, Object> filters = new TreeMap<>(SpelExpression.comparator(evaluationContext));

    @Autowired(required = false)
    protected HandlerMethodResolver handlerMethodResolver = new AnnotationHandlerMethodResolver();
    @Autowired(required = false)
    protected HandlerMethodInvoker handlerMethodInvoker = new GuardedHandlerMethodInvoker();

    public InvocationHandler invocationHandler() {
        return new Proxy();
    }

    public ConfigurableConversionService conversionService() {
        return conversionService;
    }

    @SuppressWarnings({ "unchecked" })
    public E conversionService(ConfigurableConversionService conversionService) {
        this.conversionService = conversionService;
        return (E) this;
    }

    public TaskExecutor taskExecutor() {
        return executor;
    }

    @SuppressWarnings({ "unchecked" })
    public E taskExecutor(TaskExecutor executor) {
        this.executor = executor;
        return (E) this;
    }

    @SuppressWarnings({ "unchecked" })
    public <I extends HandlerMethodInvoker> E handlerMethodInvoker(I handlerInvoker) {
        this.handlerMethodInvoker = handlerInvoker;
        return (E) this;
    }

    public HandlerMethodInvoker handlerMethodInvoker() {
        return handlerMethodInvoker;
    }

    @SuppressWarnings({ "unchecked" })
    public <R extends HandlerMethodResolver> E handlerMethodResolver(R methodResolver) {
        this.handlerMethodResolver = methodResolver;
        return (E) this;
    }

    public HandlerMethodResolver handlerMethodResolver() {
        return handlerMethodResolver;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        if (null != evaluationContext && evaluationContext instanceof StandardEvaluationContext) {
            ((StandardEvaluationContext) evaluationContext)
                    .setBeanResolver(new BeanFactoryResolver(this.applicationContext));
        }
        event("applicationContext", applicationContext);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof AutowireCapableBeanFactory) {
            this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
        }
        event("beanFactory", beanFactory);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        event("afterPropertiesSet");
    }

    public E setHandler(Object handler) {
        return handler(handler);
    }

    @SuppressWarnings({ "unchecked" })
    @Override
    public E handler(Object handler) {
        if (null != handler) {
            if (handler instanceof Collection) {
                for (Object o : (Collection) handler) {
                    handler(o);
                }
            } else if (handler instanceof Map) {
                for (Map.Entry<String, Object> entry : ((Map<String, Object>) handler).entrySet()) {
                    on(entry.getKey(), entry.getValue());
                }
            } else if (handler instanceof Class) {
                try {
                    Object o = ((Class<?>) handler).newInstance();
                    if (null != beanFactory) {
                        beanFactory.autowireBean(o);
                    }
                    handler(o);
                } catch (Throwable t) {
                    event(Events.classToEventExpression(t.getClass()), t);
                }
            } else {
                Class<?> targetClass;

                if (AopUtils.isAopProxy(handler)) {
                    targetClass = AopUtils.getTargetClass(handler);
                } else {
                    targetClass = handler.getClass();
                }

                Map<Object, HandlerMethod> handlers = handlerMethodResolver.resolve(targetClass);
                for (Map.Entry<Object, HandlerMethod> entry : handlers.entrySet()) {
                    final Object key = entry.getKey();
                    final HandlerMethod hm = entry.getValue();
                    registerHandlerMethod(key, hm, handler);
                }
            }
        }
        return (E) this;
    }

    protected void registerHandlerMethod(final Object key, final HandlerMethod method, final Object handler) {
        Handler<Object> h = new Handler<Object>() {
            @Override
            public void handle(Object obj, Object... args) {
                try {
                    if (null != obj && obj.getClass().isArray()) {
                        handlerMethodInvoker.invoke(method, handler, evaluationContext, (Object[]) obj);
                    } else {
                        handlerMethodInvoker.invoke(method, handler, evaluationContext, obj);
                    }
                } catch (Exception e) {
                    event(Events.classToEventExpression(e.getClass()), e);
                }
            }
        };
        if (key instanceof String) {
            on((String) key, h);
        } else if (key instanceof Expression) {
            on((Expression) key, h);
        } else {
            throw new IllegalArgumentException(
                    "Cannot use " + key + " for an event name (needs String or Expression).");
        }
    }

    @SuppressWarnings({ "unchecked" })
    @Override
    public E on(final String event, final Object handler) {
        Tasks.execute(new Runnable() {
            @Override
            public void run() {
                add(event, handler, handlers);
            }
        }, executor);
        return (E) this;
    }

    @SuppressWarnings({ "unchecked" })
    @Override
    public E on(final Expression expression, final Object handler) {
        Tasks.execute(new Runnable() {
            @Override
            public void run() {
                add(expression, handler, filters);
            }
        }, executor);
        return (E) this;
    }

    @SuppressWarnings({ "unchecked" })
    @Override
    public E recycle() {
        Tasks.execute(new Runnable() {
            @Override
            public void run() {
                handlers.clear();
                filters.clear();
            }
        }, executor);
        return (E) this;
    }

    @SuppressWarnings({ "unchecked" })
    @Override
    public E event(final String event, final Object... args) {
        Tasks.execute(new Runnable() {
            @Override
            public void run() {
                List<Object> handlerList = findHandlers(event);
                Object[] handlerArgs = (args.length == 0 ? new Object[] { EventedBase.this } : args);
                for (Object o : handlerList) {
                    Handlers.invoke(o, handlerArgs);
                }
            }
        }, executor);
        return (E) this;
    }

    @Override
    public boolean respondsTo(String event) {
        return findHandlers(event).size() > 0;
    }

    @SuppressWarnings({ "unchecked" })
    protected List<Object> findHandlers(Object event) {
        List<Object> handlerList = new ArrayList<>();
        // Check for assigned handlers
        Object handler = handlers.get(event);
        if (null != handler) {
            if (handler instanceof Collection) {
                handlerList.addAll((Collection) handler);
            } else {
                handlerList.add(handler);
            }
        }
        // Check for event filters
        Expression ex = null;
        if (event instanceof String && ((String) event).contains("#{")) {
            ex = new SpelExpression((String) event, evaluationContext).expression();
        } else if (event instanceof Expression) {
            ex = (Expression) event;
        }
        for (Map.Entry<Expression, Object> entry : filters.entrySet()) {
            if (Handlers.eventsMatch(evaluationContext, entry.getKey(),
                    (null != ex ? (null != evaluationContext ? ex.getValue(evaluationContext) : ex.getValue())
                            : event))) {
                handlerList.add(entry.getValue());
            }
        }
        return handlerList;
    }

    @SuppressWarnings({ "unchecked" })
    protected void add(Object key, Object val, Map m) {
        Object existing = m.get(key);
        if (null == existing) {
            m.put(key, val);
        } else if (existing instanceof Collection) {
            ((Collection) existing).add(val);
        } else {
            List<Object> l = new ArrayList<>();
            l.add(existing);
            l.add(val);
            m.put(key, l);
        }
    }

    private class Proxy implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String name = method.getName();
            On on;
            if (name.matches("on[A-Z](.*)")) {
                String eventName = StringUtils.uncapitalize(method.getName().substring(2));
                for (Object o : args) {
                    on(eventName, o);
                }
                return proxy;
            }

            if (null != (on = AnnotationUtils.findAnnotation(method, On.class))) {
                String s = on.value();
                if (s.contains("#{")) {
                    Expression x = $(s);
                    for (Object o : args) {
                        on(x, o);
                    }
                } else {
                    for (Object o : args) {
                        on(s, o);
                    }
                }
                return proxy;
            }

            switch (name) {
            default:
                event(name, args);
            }
            return proxy;
        }
    }

}