Java tutorial
/* * 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; } } }