001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.hooks; 019 020import io.stallion.exceptions.UsageException; 021import io.stallion.monitoring.HealthTrackingHookHandler; 022import io.stallion.restfulEndpoints.XSRFHooks; 023import io.stallion.services.Log; 024import io.stallion.settings.Settings; 025 026import java.lang.reflect.Modifier; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import static io.stallion.utils.Literals.*; 032 033 034public class HookRegistry { 035 private Map<Class<? extends HookHandler>, List<HookHandler>> hooksByClass = new HashMap<>(); 036 private Map<Class<? extends ChainedHook>, List<ChainedHook>> chainsByClass = new HashMap<>(); 037 private Map<Class<? extends FirstValueHook>, List<FirstValueHook>> firstValueHooksByClass = new HashMap<>(); 038 039 private static HookRegistry _instance; 040 041 public static HookRegistry instance() { 042 if (_instance == null) { 043 throw new UsageException("HookRegistry "); 044 } 045 return _instance; 046 } 047 048 public static HookRegistry load() { 049 _instance = new HookRegistry(); 050 registerDefaultHandlers(); 051 return _instance; 052 } 053 054 public static void shutdown() { 055 _instance = null; 056 } 057 058 public static void registerDefaultHandlers() { 059 XSRFHooks.register(); 060 instance().register(new HealthTrackingHookHandler()); 061 } 062 063 public void register(HookHandler handler) { 064 Class base = getAbstractBaseClass(handler.getClass()); 065 066 if (base == null) { 067 throw new UsageException("You tried to register a handler that does not inherit from an abstract handler class. There is no way this handler can be triggered."); 068 } 069 070 if (!hooksByClass.containsKey(base)) { 071 hooksByClass.put(base, list()); 072 } 073 if (!hooksByClass.get(base).contains(handler)) { 074 hooksByClass.get(base).add(handler); 075 } 076 } 077 078 public void register(ChainedHook handler) { 079 Class base = getAbstractBaseClass(handler.getClass()); 080 if (base == null) { 081 throw new UsageException("You tried to register a handler that does not inherit from an abstract handler class. There is no way this handler can be triggered."); 082 } 083 if (!chainsByClass.containsKey(handler.getClass())) { 084 chainsByClass.put(base, list()); 085 } 086 if (!chainsByClass.get(base).contains(handler)) { 087 chainsByClass.get(base).add(handler); 088 } 089 } 090 091 092 public void register(FirstValueHook handler) { 093 Class base = getAbstractBaseClass(handler.getClass()); 094 if (base == null) { 095 throw new UsageException("You tried to register a handler that does not inherit from an abstract handler class. There is no way this handler can be triggered."); 096 } 097 if (!firstValueHooksByClass.containsKey(handler.getClass())) { 098 firstValueHooksByClass.put(base, list()); 099 } 100 if (!firstValueHooksByClass.get(base).contains(handler)) { 101 firstValueHooksByClass.get(base).add(handler); 102 } 103 } 104 105 106 private Class getAbstractBaseClass(Class cls) { 107 Class base = cls.getSuperclass(); 108 if (base.isAssignableFrom(HookHandler.class)) { 109 return null; 110 } 111 if (base.isAssignableFrom(ChainedHook.class)) { 112 return null; 113 } 114 if (base.isAssignableFrom(FirstValueHook.class)) { 115 return null; 116 } 117 118 for (int x=0; x< 10;x++) { 119 if (base == null) { 120 return null; 121 } 122 if (Modifier.isAbstract(base.getModifiers())) { 123 return base; 124 } 125 base = cls.getSuperclass(); 126 } 127 return null; 128 } 129 130 public <T, V> T find(Class<? extends FirstValueHook<T, V>> cls, V arg) { 131 if (firstValueHooksByClass.containsKey(cls)) { 132 for (FirstValueHook<T, V> hook: firstValueHooksByClass.get(cls)) { 133 T val = hook.find(arg); 134 if (val != null) { 135 return val; 136 } 137 } 138 } 139 return null; 140 } 141 142 public <T> void dispatch(Class<? extends HookHandler> handlerClass, T arg) { 143 if (!Modifier.isAbstract(handlerClass.getModifiers())) { 144 throw new UsageException("You can only dispatch events for an abstract class. Registered handlers will create implementations of this abstract class and then register them to be run when your event is dispatched."); 145 } 146 if (hooksByClass.containsKey(handlerClass)) { 147 for (HookHandler handler : hooksByClass.get(handlerClass)) { 148 try { 149 handler.handle(arg); 150 } catch (Exception e) { 151 if (Settings.instance().isStrict()) { 152 throw new RuntimeException(e); 153 } else { 154 Log.exception(e, "Exception running handler {0} with arg {1}", handler.getClass().getName(), arg); 155 } 156 } 157 } 158 } 159 } 160 161 public <T> T chain(Class<? extends ChainedHook> chainClass, T arg) { 162 if (!Modifier.isAbstract(chainClass.getModifiers())) { 163 throw new UsageException("You can only dispatch events for an abstract class. Registered handlers will create implementations of this abstract class and then register them to be run when your event is dispatched."); 164 } 165 if (chainsByClass.containsKey(chainClass)) { 166 for (ChainedHook chain : chainsByClass.get(chainClass)) { 167 arg = (T)chain.chain(arg); 168 } 169 } 170 return arg; 171 } 172}