/** * */ package jframe.core.plugin.dispatch; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jframe.core.dispatch.DispatchTarget; import jframe.core.msg.ConfigMsg; import jframe.core.msg.Msg; import jframe.core.msg.PoisonMsg; import jframe.core.plugin.Plugin; import jframe.core.plugin.PluginRef; import jframe.core.plugin.annotation.Message; import jframe.core.plugin.annotation.MsgInterest; import jframe.core.plugin.annotation.MsgRecv; /** * TODO types filter * * @author dzh * @date Oct 9, 2013 4:04:58 PM * @since 1.0 */ public class DispatchTargetHandler implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(DispatchTargetHandler.class); private static final String M_Receive = "receive"; private static final String M_InterestMsg = "interestMsg"; private PluginRef _ref; private BlockingQueue<Msg<?>> _cache; // message types private int[] _types; private Map<String, WeakReference<Method>> _mc = new WeakHashMap<String, WeakReference<Method>>(6); public DispatchTargetHandler(PluginRef ref) { if (ref == null) { throw new NullPointerException("PluginRef is null"); } this._ref = ref; update(ref.getPlugin()); } public void update(Plugin plugin) { if (plugin == null) return; _mc.clear(); Message ma = plugin.getClass().getAnnotation(Message.class); _types = ma.msgTypes(); if (_types != null) Arrays.sort(_types); int maxCache = ma.msgMaxCache(); if (_cache == null) { _cache = new LinkedBlockingDeque<Msg<?>>(maxCache); } else if (maxCache != (_cache.size() + _cache.remainingCapacity())) { // TODO miss message BlockingQueue<Msg<?>> cache = new LinkedBlockingDeque<Msg<?>>(maxCache); _cache.drainTo(cache, maxCache); _cache = cache; } sendCache(); } void sendCache() { if (_cache == null || _cache.size() == 0 || getPlugin() == null) return; final Plugin p = getPlugin(); final BlockingQueue<Msg<?>> cache = _cache; new Thread("SendCacheMsg") { public void run() { Method im = null; // interest method Method rm = null; // receive method for (Method m : getPlugin().getClass().getDeclaredMethods()) { if (m.getAnnotation(MsgInterest.class) != null) { im = m; m.setAccessible(true); } else if (m.getAnnotation(MsgRecv.class) != null) { rm = m; m.setAccessible(true); } if (im != null && rm != null) { break; } } Msg<?> msg = null; while ((msg = cache.poll()) != null) { if (interestMsg(msg)) { // message type if (im != null) { try { if (!((Boolean) im.invoke(p, msg)).booleanValue()) continue; } catch (Exception e) { LOG.warn(e.getMessage()); } } if (rm != null) { try { rm.invoke(p, msg); LOG.info("PluginRef send cache msg: {}", msg.toString()); } catch (Exception e) { LOG.warn(e.getMessage()); } } try { Thread.sleep(2); } catch (InterruptedException e) { } } } } }.start(); } public Plugin getPlugin() { return _ref.getPlugin(); } /* * (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 { // cache msg when plugin is updating if (_ref.isUpdating() && method.getName().equals(M_InterestMsg) && args.length > 0 && args[0] instanceof Msg) { // TODO pitfall store nasty msg when not use msgTypes if (interestMsg((Msg<?>) args[0])) { LOG.info("PluginRef recv cache msg: {}", args[0].toString()); _cache.offer((Msg<?>) args[0]);// TODO queue is full } return false; } Plugin p = _ref.getPlugin(); if (p == null) { // when plug-in is updating, p is null TODO return null; } if (method.equals(DispatchTarget.class.getDeclaredMethod(M_InterestMsg, new Class[] { Msg.class }))) { if (args.length > 0 && args[0] instanceof Msg) { // special message,default return false try { if ((Msg<?>) args[0] instanceof PoisonMsg) { return _ref.getPlugin().getClass().getAnnotation(Message.class).recvPoison(); } else if ((Msg<?>) args[0] instanceof ConfigMsg) { return _ref.getPlugin().getClass().getAnnotation(Message.class).recvConfig(); } } catch (Exception e) { return false; } // custom message if (!interestMsg((Msg<?>) args[0])) return false; Method cm = getCacheMethod(M_InterestMsg); if (cm != null) return cm.invoke(p, args); Class<?> clazz = p.getClass(); do { for (Method m : clazz.getDeclaredMethods()) { if (m.getAnnotation(MsgInterest.class) != null) { m.setAccessible(true); putCacheMethod(M_InterestMsg, m); return m.invoke(p, args); } } } while ((clazz = clazz.getSuperclass()) != null); } return false; } else if (method.equals(DispatchTarget.class.getDeclaredMethod(M_Receive, new Class[] { Msg.class }))) { Method cm = getCacheMethod(M_Receive); if (cm != null) return cm.invoke(p, args); Class<?> clazz = p.getClass(); do { for (Method m : clazz.getDeclaredMethods()) { if (m.getAnnotation(MsgRecv.class) != null) { m.setAccessible(true); putCacheMethod(M_Receive, m); return m.invoke(p, args); } } } while ((clazz = clazz.getSuperclass()) != null); } return method.invoke(p, args); } /** * @param msg * @return */ boolean interestMsg(Msg<?> msg) { if (_types == null || _types.length == 0) return true; return Arrays.binarySearch(_types, msg.getType()) < 0 ? false : true; } private Method getCacheMethod(String name) { WeakReference<Method> wr = _mc.get(name); return null == wr ? null : wr.get(); } private void putCacheMethod(String name, Method m) { _mc.put(name, new WeakReference<Method>(m)); } }