/xian-core/src/main/java/info/xiancloud/core/LocalUnitsManager.java

https://github.com/happyyangyuan/xian · Java · 257 lines · 175 code · 22 blank · 60 comment · 20 complexity · 95d77418a1ea6a33c55c6fd1f64294c1 MD5 · raw file

  1. package info.xiancloud.core;
  2. import com.alibaba.fastjson.JSON;
  3. import info.xiancloud.core.distribution.UnitProxy;
  4. import info.xiancloud.core.util.Reflection;
  5. import info.xiancloud.core.util.StringUtil;
  6. import info.xiancloud.core.distribution.UnitProxy;
  7. import info.xiancloud.core.util.Reflection;
  8. import info.xiancloud.core.util.StringUtil;
  9. import java.util.*;
  10. import java.util.concurrent.CopyOnWriteArrayList;
  11. import java.util.concurrent.locks.ReadWriteLock;
  12. import java.util.concurrent.locks.ReentrantReadWriteLock;
  13. import java.util.function.Consumer;
  14. import java.util.function.Function;
  15. /**
  16. * Local management of the units.
  17. *
  18. * @author happyyangyuan
  19. */
  20. public abstract class LocalUnitsManager {
  21. /**
  22. * For thread-safe consideration.
  23. * This lock make support for dynamically changing local registered unit collection.
  24. * eg. dynamical unit aop operation may change the unit collections concurrently in runtime situation.
  25. */
  26. private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  27. /**
  28. * for searching local unit list within the same group
  29. */
  30. private static Map<String, List<Unit>> unitMap;
  31. //for searching local unit instance by full unit name
  32. private static Map<String, Unit> searchUnitMap;
  33. //for searching local group instance by unit class
  34. private static Map<Class<? extends Unit>, Group> searchGroupByUnitClass;
  35. //for searching local group instance by group name
  36. private static Map<String, Group> searchGroupByNameMap;
  37. //for searching local unit instances use unit class
  38. private static Map<Class<? extends Unit>, Unit> searchUnitByClass;
  39. static {
  40. try {
  41. unitMap = new HashMap<String, List<Unit>>() {
  42. @Override
  43. public String toString() {
  44. Map<String, List<String>> map = new HashMap<>();
  45. for (String serviceName : unitMap.keySet()) {
  46. List<String> unitNames = new ArrayList<>();
  47. for (Unit unit : unitMap.get(serviceName)) {
  48. unitNames.add(unit.getName());
  49. }
  50. map.put(serviceName, unitNames);
  51. }
  52. return JSON.toJSONString(map);
  53. }
  54. };
  55. searchUnitMap = new HashMap<>();
  56. searchGroupByUnitClass = new HashMap<>();
  57. searchGroupByNameMap = new HashMap<>();
  58. searchUnitByClass = new HashMap<>();
  59. List<Unit> allUnitList = Reflection.getSubClassInstances(Unit.class);
  60. //add extra units here
  61. for (ExtraUnitProvider extraUnitProvider : Reflection.getSubClassInstances(ExtraUnitProvider.class)) {
  62. allUnitList.addAll(extraUnitProvider.provideExtraUnits());
  63. }
  64. for (Unit unit : allUnitList) {
  65. if (UnitProxy.class == unit.getClass()) {
  66. //UnitProxy is not unit really, it is just a bean used to cache unit definition.
  67. continue;
  68. }
  69. if (unit.getMeta() == null || StringUtil.isEmpty(unit.getName())) {
  70. System.err.println(String.format("unit %s's name is null! Please check! %s is ignored!",
  71. unit.getClass().getSimpleName(),
  72. unit.getClass().getSimpleName()));
  73. continue;
  74. }
  75. if (unit.getName().contains(Unit.SEPARATOR)) {
  76. System.err.println("Unit name can not contain " + Unit.SEPARATOR + " :" + unit.getName());
  77. continue;
  78. }
  79. if (unit.getGroup() == null) {
  80. System.err.println(String.format("No group is specified for the unit with name %s! This unit is ignored!", unit.getName()));
  81. continue;
  82. }
  83. Group group = unit.getGroup();
  84. if (StringUtil.isEmpty(group.getName())) {
  85. System.err.println(String.format("group: %s.getName() returns null! %s.%s is ignored!",
  86. group.getClass().getSimpleName(),
  87. group.getClass().getSimpleName(),
  88. unit.getClass().getSimpleName()));
  89. continue;
  90. }
  91. if (group.getName().contains(Unit.SEPARATOR)) {
  92. System.err.println(Unit.SEPARATOR + " is not allowed in group name: " + group.getName());
  93. continue;
  94. }
  95. if (unitMap.get(group.getName()) != null) {
  96. unitMap.get(group.getName()).add(unit);
  97. } else {
  98. //我们引入了动态aop功能,它会在运行时修改静态全局变量ServiceManager.unitMap,而这个map以及map内的list是被N线程并行读的,因此这最好使用并发安全并且读成本低的CopyOnWriteArrayList
  99. List<Unit> list = new CopyOnWriteArrayList<>();
  100. list.add(unit);
  101. unitMap.put(group.getName(), list);
  102. }
  103. searchUnitMap.put(group.getName() + Unit.SEPARATOR + unit.getName(), unit);
  104. searchGroupByUnitClass.put(unit.getClass(), group);
  105. searchGroupByNameMap.put(group.getName(), group);
  106. searchUnitByClass.put(unit.getClass(), unit);
  107. }
  108. System.out.println(LocalUnitsManager.class.getSimpleName() + " has finished to scan all the units: " + unitMap.toString());
  109. } catch (Exception e) {
  110. System.err.println(LocalUnitsManager.class.getSimpleName() + " failed to scan all units, System exits with code: " + Constant.SYSTEM_EXIT_CODE_FOR_SYS_INIT_ERROR);
  111. e.printStackTrace();
  112. System.exit(Constant.SYSTEM_EXIT_CODE_FOR_SYS_INIT_ERROR);
  113. }
  114. }
  115. /**
  116. * Dynamically change the unit collections. Currently we use this for aop.
  117. */
  118. public static void replaceUnit(/*String group, String unitName, */ Unit newUnit) {
  119. String group = newUnit.getGroup().getName(), unitName = newUnit.getName();
  120. readWriteLock.writeLock().lock();
  121. try {
  122. //unitMap
  123. List<Unit> unitList = unitMap.get(group);
  124. unitList.removeIf(unitToRemove -> Objects.equals(unitToRemove.getName(), unitName));
  125. unitList.add(newUnit);
  126. //searchUnitByClassMap
  127. Map<Class<? extends Unit>, Unit> tmp = new HashMap<>();
  128. for (Map.Entry<Class<? extends Unit>, Unit> classUnitEntry : searchUnitByClass.entrySet()) {
  129. Unit unit = classUnitEntry.getValue();
  130. if (unit.getGroup().getName().equals(group) && unit.getName().equals(unitName)) {
  131. tmp.put(classUnitEntry.getKey(), newUnit);
  132. break;
  133. }
  134. }
  135. searchUnitByClass.putAll(tmp);
  136. //searchUnitMap
  137. searchUnitMap.put(Unit.fullName(group, unitName), newUnit);
  138. } finally {
  139. readWriteLock.writeLock().unlock();
  140. }
  141. }
  142. /**
  143. * return the cached group singleton of the given unit class.
  144. */
  145. public static Group getGroupByUnitClass(Class<? extends Unit> unitClass) {
  146. readWriteLock.readLock().lock();
  147. try {
  148. return searchGroupByUnitClass.get(unitClass);
  149. } finally {
  150. readWriteLock.readLock().unlock();
  151. }
  152. }
  153. /**
  154. * return the cached unit singleton of the given unit class.
  155. */
  156. public static Unit getUnitByUnitClass(Class<? extends Unit> unitClass) {
  157. readWriteLock.readLock().lock();
  158. try {
  159. return searchUnitByClass.get(unitClass);
  160. } finally {
  161. readWriteLock.readLock().unlock();
  162. }
  163. }
  164. /**
  165. * @return the cached group singleton of the given name.
  166. */
  167. public static Group getGroupByName(String name) {
  168. readWriteLock.readLock().lock();
  169. try {
  170. return searchGroupByNameMap.get(name);
  171. } finally {
  172. readWriteLock.readLock().unlock();
  173. }
  174. }
  175. /**
  176. * Iterate the {@link #unitMap local unit map} thread-safely.
  177. * This iteration is for you to read the map, not to modify the map.
  178. *
  179. * @param consumer the consumer for you to operate the map.
  180. */
  181. public static void unitMap(Consumer<Map<String, List<Unit>>> consumer) {
  182. readWriteLock.readLock().lock();
  183. try {
  184. consumer.accept(Collections.unmodifiableMap(unitMap));
  185. } finally {
  186. readWriteLock.readLock().unlock();
  187. }
  188. }
  189. /**
  190. * Iterate the {@link #unitMap local unit map} thread-safely.
  191. * This iteration is for you to read the map, not to modify the map.
  192. *
  193. * @param function the function whatever you want to do with the unit map.
  194. */
  195. public static <T> T unitMap(Function<Map<String, List<Unit>>, T> function) {
  196. readWriteLock.readLock().lock();
  197. try {
  198. return function.apply(Collections.unmodifiableMap(unitMap));
  199. } finally {
  200. readWriteLock.readLock().unlock();
  201. }
  202. }
  203. /**
  204. * Iterate {@link #searchUnitMap} thread-safely.
  205. *
  206. * @param consumer the operation for the unit map.
  207. */
  208. public static void searchUnitMap(Consumer<Map<String, Unit>> consumer) {
  209. readWriteLock.readLock().lock();
  210. try {
  211. consumer.accept(Collections.unmodifiableMap(searchUnitMap));
  212. } finally {
  213. readWriteLock.readLock().unlock();
  214. }
  215. }
  216. /**
  217. * @param fullUnitName group.unit
  218. * @return the local cached unit object, or null if not found.
  219. */
  220. public static Unit getLocalUnit(String fullUnitName) {
  221. readWriteLock.readLock().lock();
  222. try {
  223. return searchUnitMap.get(fullUnitName);
  224. } finally {
  225. readWriteLock.readLock().unlock();
  226. }
  227. }
  228. /**
  229. * Get local cached unit singleton.
  230. * the same as {@link #getLocalUnit(String)}
  231. */
  232. public static Unit getLocalUnit(String groupName, String unitName) {
  233. return getLocalUnit(Unit.fullName(groupName, unitName));
  234. }
  235. }