ExtensionLoader.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. package cn.nosum.common.extension;
  2. import cn.nosum.common.annotation.Activate;
  3. import cn.nosum.common.annotation.Adaptive;
  4. import cn.nosum.common.annotation.DisableInject;
  5. import cn.nosum.common.annotation.SPI;
  6. import cn.nosum.common.util.*;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import java.io.BufferedReader;
  10. import java.io.InputStreamReader;
  11. import java.lang.reflect.Method;
  12. import java.lang.reflect.Modifier;
  13. import java.net.URL;
  14. import java.nio.charset.StandardCharsets;
  15. import java.util.*;
  16. import java.util.concurrent.ConcurrentHashMap;
  17. import java.util.concurrent.ConcurrentMap;
  18. import java.util.regex.Pattern;
  19. /**
  20. * Load gateway ExtensionLoader
  21. * @param <T>
  22. */
  23. public class ExtensionLoader<T> {
  24. private static final Logger logger= LoggerFactory.getLogger(ExtensionLoader.class);
  25. // 需要扫描的路径
  26. private static final String GATEWAY_DIRECTORY = "META-INF/gateway/";
  27. private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
  28. // 缓存
  29. private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
  30. private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
  31. private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
  32. private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
  33. private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
  34. private Set<Class<?>> cachedWrapperClasses;
  35. private final ExtensionFactory objectFactory;
  36. private volatile Class<?> cachedAdaptiveClass = null;
  37. private final Class<?> type;
  38. private String cachedDefaultName;
  39. // 记录错误信息
  40. private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
  41. private volatile Throwable createAdaptiveInstanceError;
  42. private ExtensionLoader(Class<?> type) {
  43. this.type = type;
  44. objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
  45. }
  46. @SuppressWarnings("unchecked")
  47. public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
  48. if (type == null) {
  49. throw new IllegalArgumentException("Extension type == null");
  50. }
  51. if (!type.isInterface()) {
  52. throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
  53. }
  54. if (!withExtensionAnnotation(type)) {
  55. throw new IllegalArgumentException("Extension type (" + type +
  56. ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
  57. }
  58. ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  59. if (loader == null) {
  60. EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
  61. loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  62. }
  63. return loader;
  64. }
  65. @SuppressWarnings("unchecked")
  66. public T getExtension(String name) {
  67. if (StringUtils.isEmpty(name)) {
  68. throw new IllegalArgumentException("Extension name == null");
  69. }
  70. if ("true".equals(name)) {
  71. return getDefaultExtension();
  72. }
  73. final Holder<Object> holder = getOrCreateHolder(name);
  74. Object instance = holder.get();
  75. if (instance == null) {
  76. synchronized (holder) {
  77. instance = holder.get();
  78. if (instance == null) {
  79. instance = createExtension(name);
  80. holder.set(instance);
  81. }
  82. }
  83. }
  84. return (T) instance;
  85. }
  86. private Holder<Object> getOrCreateHolder(String name) {
  87. Holder<Object> holder = cachedInstances.get(name);
  88. if (holder == null) {
  89. cachedInstances.putIfAbsent(name, new Holder<>());
  90. holder = cachedInstances.get(name);
  91. }
  92. return holder;
  93. }
  94. private T createExtension(String name) {
  95. Class<?> clazz = getExtensionClasses().get(name);
  96. if (clazz == null) {
  97. throw new NullPointerException(name);
  98. }
  99. try {
  100. T instance = (T) EXTENSION_INSTANCES.get(clazz);
  101. if (instance == null) {
  102. EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
  103. instance = (T) EXTENSION_INSTANCES.get(clazz);
  104. }
  105. injectExtension(instance);
  106. Set<Class<?>> wrapperClasses = cachedWrapperClasses;
  107. if (CollectionUtils.isNotEmpty(wrapperClasses)) {
  108. for (Class<?> wrapperClass : wrapperClasses) {
  109. instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
  110. }
  111. }
  112. return instance;
  113. } catch (Throwable t) {
  114. throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
  115. type + ") couldn't be instantiated: " + t.getMessage(), t);
  116. }
  117. }
  118. /**
  119. * 实现依赖注入
  120. * @param instance 需要进行依赖注入的实例
  121. */
  122. private T injectExtension(T instance) {
  123. try {
  124. if (objectFactory != null) {
  125. for (Method method : instance.getClass().getMethods()) {
  126. if (isSetter(method)) {
  127. // 存在 @DisableInject 则跳过
  128. if (method.getAnnotation(DisableInject.class) != null) {
  129. continue;
  130. }
  131. Class<?> pt = method.getParameterTypes()[0];
  132. if (ReflectUtils.isPrimitives(pt)) {
  133. continue;
  134. }
  135. try {
  136. String property = getSetterProperty(method);
  137. Object object = objectFactory.getExtension(pt, property);
  138. if (object != null) {
  139. method.invoke(instance, object);
  140. }
  141. } catch (Exception e) {
  142. logger.error("Failed to inject via method " + method.getName()
  143. + " of interface " + type.getName() + ": " + e.getMessage(), e);
  144. }
  145. }
  146. }
  147. }
  148. } catch (Exception e) {
  149. logger.error(e.getMessage(), e);
  150. }
  151. return instance;
  152. }
  153. /**
  154. * @return 根据默认扩展名获取到的实例
  155. */
  156. public T getDefaultExtension() {
  157. getExtensionClasses();
  158. if (StringUtils.isEmpty(cachedDefaultName) || "true".equals(cachedDefaultName)) {
  159. return null;
  160. }
  161. return getExtension(cachedDefaultName);
  162. }
  163. /**
  164. * @return 获取自适应扩展点
  165. */
  166. @SuppressWarnings("unchecked")
  167. public T getAdaptiveExtension() {
  168. Object instance = cachedAdaptiveInstance.get();
  169. if (instance == null) {
  170. if (createAdaptiveInstanceError == null) {
  171. synchronized (cachedAdaptiveInstance) {
  172. instance = cachedAdaptiveInstance.get();
  173. if (instance == null) {
  174. try {
  175. instance = createAdaptiveExtension();
  176. cachedAdaptiveInstance.set(instance);
  177. } catch (Throwable t) {
  178. createAdaptiveInstanceError = t;
  179. throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
  180. }
  181. }
  182. }
  183. } else {
  184. throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
  185. }
  186. }
  187. return (T) instance;
  188. }
  189. @SuppressWarnings("unchecked")
  190. private T createAdaptiveExtension() {
  191. try {
  192. return injectExtension((T) getAdaptiveExtensionClass().newInstance());
  193. } catch (Exception e) {
  194. throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
  195. }
  196. }
  197. /**
  198. * 获取自适应扩展点
  199. */
  200. private Class<?> getAdaptiveExtensionClass() {
  201. getExtensionClasses();
  202. if (cachedAdaptiveClass != null) {
  203. return cachedAdaptiveClass;
  204. }
  205. return null;
  206. }
  207. private Map<String, Class<?>> getExtensionClasses() {
  208. Map<String, Class<?>> classes = cachedClasses.get();
  209. if (classes == null) {
  210. synchronized (cachedClasses) {
  211. classes = cachedClasses.get();
  212. if (classes == null) {
  213. classes = loadExtensionClasses();
  214. cachedClasses.set(classes);
  215. }
  216. }
  217. }
  218. return classes;
  219. }
  220. /**
  221. * 如果存在默认扩展名,提取并且缓存
  222. */
  223. private void cacheDefaultExtensionName() {
  224. String value;
  225. if (StringUtils.isEmpty(value=PropertiesUtil.getProperty(type.getName()))){
  226. final SPI defaultAnnotation = type.getAnnotation(SPI.class);
  227. if (defaultAnnotation != null) {
  228. value = defaultAnnotation.value();
  229. if ((value = value.trim()).length() > 0) {
  230. String[] names = NAME_SEPARATOR.split(value);
  231. if (names.length > 1) {
  232. throw new IllegalStateException("More than 1 default extension name on extension "
  233. + type.getName() + ": "
  234. + Arrays.toString(names));
  235. }
  236. if (names.length == 1) {
  237. cachedDefaultName = names[0];
  238. }
  239. }
  240. }
  241. }else{
  242. cachedDefaultName=value;
  243. }
  244. }
  245. // synchronized in getExtensionClasses
  246. private Map<String, Class<?>> loadExtensionClasses() {
  247. cacheDefaultExtensionName();
  248. Map<String, Class<?>> extensionClasses = new HashMap<>();
  249. loadDirectory(extensionClasses, GATEWAY_DIRECTORY, type.getName());
  250. return extensionClasses;
  251. }
  252. private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
  253. String fileName = dir + type;
  254. try {
  255. Enumeration<URL> urls;
  256. ClassLoader classLoader = findClassLoader();
  257. if (classLoader != null) {
  258. urls = classLoader.getResources(fileName);
  259. } else {
  260. urls = ClassLoader.getSystemResources(fileName);
  261. }
  262. if (urls != null) {
  263. while (urls.hasMoreElements()) {
  264. java.net.URL resourceURL = urls.nextElement();
  265. loadResource(extensionClasses, classLoader, resourceURL);
  266. }
  267. }
  268. } catch (Throwable t) {
  269. logger.error("Exception occurred when loading extension class (interface: " +
  270. type + ", description file: " + fileName + ").", t);
  271. }
  272. }
  273. private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
  274. try {
  275. try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
  276. String line;
  277. while ((line = reader.readLine()) != null) {
  278. final int ci = line.indexOf('#');
  279. if (ci >= 0) {
  280. line = line.substring(0, ci);
  281. }
  282. line = line.trim();
  283. if (line.length() > 0) {
  284. try {
  285. String name = null;
  286. int i = line.indexOf('=');
  287. if (i > 0) {
  288. name = line.substring(0, i).trim();
  289. line = line.substring(i + 1).trim();
  290. }
  291. if (line.length() > 0) {
  292. loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
  293. }
  294. } catch (Throwable t) {
  295. IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
  296. exceptions.put(line, e);
  297. }
  298. }
  299. }
  300. }
  301. } catch (Throwable t) {
  302. logger.error("Exception occurred when loading extension class (interface: " +
  303. type + ", class file: " + resourceURL + ") in " + resourceURL, t);
  304. }
  305. }
  306. private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
  307. if (!type.isAssignableFrom(clazz)) {
  308. throw new IllegalStateException("Error occurred when loading extension class (interface: " +
  309. type + ", class line: " + clazz.getName() + "), class "
  310. + clazz.getName() + " is not subtype of interface.");
  311. }
  312. if (clazz.isAnnotationPresent(Adaptive.class)) {
  313. cacheAdaptiveClass(clazz,name);
  314. } else if (isWrapperClass(clazz)) {
  315. cacheWrapperClass(clazz);
  316. }
  317. }
  318. private void cacheAdaptiveClass(Class<?> clazz,String name) {
  319. // 只有名称匹配时才会进行保存
  320. if (cachedAdaptiveClass == null && name.equals(cachedDefaultName)) {
  321. cachedAdaptiveClass = clazz;
  322. } else if (!cachedAdaptiveClass.equals(clazz)) {
  323. throw new IllegalStateException("More than 1 adaptive class found: "
  324. + cachedAdaptiveClass.getClass().getName()
  325. + ", " + clazz.getClass().getName());
  326. }
  327. }
  328. private void cacheWrapperClass(Class<?> clazz) {
  329. if (cachedWrapperClasses == null) {
  330. cachedWrapperClasses = new HashSet<>();
  331. }
  332. cachedWrapperClasses.add(clazz);
  333. }
  334. private boolean isWrapperClass(Class<?> clazz) {
  335. try {
  336. clazz.getConstructor(type);
  337. return true;
  338. } catch (NoSuchMethodException e) {
  339. return false;
  340. }
  341. }
  342. private boolean isSetter(Method method) {
  343. return method.getName().startsWith("set")
  344. && method.getParameterTypes().length == 1
  345. && Modifier.isPublic(method.getModifiers());
  346. }
  347. private static ClassLoader findClassLoader() {
  348. return ClassUtils.getClassLoader(ExtensionLoader.class);
  349. }
  350. private static <T> boolean withExtensionAnnotation(Class<T> type) {
  351. return type.isAnnotationPresent(SPI.class);
  352. }
  353. private String getSetterProperty(Method method) {
  354. return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
  355. }
  356. public Set<String> getSupportedExtensions() {
  357. Map<String, Class<?>> clazzes = getExtensionClasses();
  358. return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
  359. }
  360. }