diff --git a/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java b/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java index a6be35a41e3..36cc6c846ef 100644 --- a/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java +++ b/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java @@ -20,19 +20,17 @@ import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.List; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; import io.seata.common.Constants; import io.seata.common.executor.Initialize; import io.seata.common.util.CollectionUtils; -import io.seata.common.util.IOUtil; -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,11 +41,6 @@ * @author slievrly */ public class EnhancedServiceLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedServiceLoader.class); - private static final String SERVICES_DIRECTORY = "META-INF/services/"; - private static final String SEATA_DIRECTORY = "META-INF/seata/"; - @SuppressWarnings("rawtypes") - private static Map> providers = new ConcurrentHashMap<>(); /** * Specify classLoader to load the service provider @@ -59,7 +52,7 @@ public class EnhancedServiceLoader { * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, ClassLoader loader) throws EnhancedServiceNotFoundException { - return loadFile(service, null, loader); + return InnerEnhancedServiceLoader.getServiceLoader(service).load(loader); } /** @@ -71,7 +64,7 @@ public static S load(Class service, ClassLoader loader) throws EnhancedSe * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service) throws EnhancedServiceNotFoundException { - return loadFile(service, null, findClassLoader()); + return InnerEnhancedServiceLoader.getServiceLoader(service).load(findClassLoader()); } /** @@ -84,7 +77,7 @@ public static S load(Class service) throws EnhancedServiceNotFoundExcepti * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, String activateName) throws EnhancedServiceNotFoundException { - return loadFile(service, activateName, findClassLoader()); + return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, findClassLoader()); } /** @@ -98,8 +91,8 @@ public static S load(Class service, String activateName) throws EnhancedS * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, String activateName, ClassLoader loader) - throws EnhancedServiceNotFoundException { - return loadFile(service, activateName, loader); + throws EnhancedServiceNotFoundException { + return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, loader); } /** @@ -113,15 +106,8 @@ public static S load(Class service, String activateName, ClassLoader load * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, String activateName, Object[] args) - throws EnhancedServiceNotFoundException { - Class[] argsType = null; - if (args != null && args.length > 0) { - argsType = new Class[args.length]; - for (int i = 0; i < args.length; i++) { - argsType[i] = args[i].getClass(); - } - } - return loadFile(service, activateName, findClassLoader(), argsType, args); + throws EnhancedServiceNotFoundException { + return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, args, findClassLoader()); } /** @@ -136,8 +122,8 @@ public static S load(Class service, String activateName, Object[] args) * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, String activateName, Class[] argsType, Object[] args) - throws EnhancedServiceNotFoundException { - return loadFile(service, activateName, findClassLoader(), argsType, args); + throws EnhancedServiceNotFoundException { + return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, argsType, args, findClassLoader()); } /** @@ -148,32 +134,20 @@ public static S load(Class service, String activateName, Class[] argsType * @return list list */ public static List loadAll(Class service) { - return loadAll(service, null, null); + return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(findClassLoader()); } /** * get all implements * - * @param the type parameter - * @param service the service - * @param argsType the args type - * @param args the args + * @param the type parameter + * @param service the service + * @param argsType the args type + * @param args the args * @return list list */ public static List loadAll(Class service, Class[] argsType, Object[] args) { - List allInstances = new ArrayList<>(); - List allClazzs = getAllExtensionClass(service); - if (CollectionUtils.isEmpty(allClazzs)) { - return allInstances; - } - try { - for (Class clazz : allClazzs) { - allInstances.add(initInstance(service, clazz, argsType, args)); - } - } catch (Throwable t) { - throw new EnhancedServiceNotFoundException(t); - } - return allInstances; + return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(argsType, args, findClassLoader()); } /** @@ -185,7 +159,7 @@ public static List loadAll(Class service, Class[] argsType, Object[] a */ @SuppressWarnings("rawtypes") static List getAllExtensionClass(Class service) { - return findAllExtensionClass(service, null, findClassLoader()); + return InnerEnhancedServiceLoader.getServiceLoader(service).getAllExtensionClass(findClassLoader()); } /** @@ -198,181 +172,412 @@ static List getAllExtensionClass(Class service) { */ @SuppressWarnings("rawtypes") static List getAllExtensionClass(Class service, ClassLoader loader) { - return findAllExtensionClass(service, null, loader); + return InnerEnhancedServiceLoader.getServiceLoader(service).getAllExtensionClass(loader); } - private static S loadFile(Class service, String activateName, ClassLoader loader) { - return loadFile(service, activateName, loader, null, null); + /** + * Cannot use TCCL, in the pandora container will cause the class in the plugin not to be loaded + * + * @return + */ + private static ClassLoader findClassLoader() { + return EnhancedServiceLoader.class.getClassLoader(); } - @SuppressWarnings("rawtypes") - private static S loadFile(Class service, String activateName, ClassLoader loader, Class[] argTypes, - Object[] args) { - try { - boolean foundFromCache = true; - List extensions = providers.get(service); - if (extensions == null) { - synchronized (service) { - extensions = providers.get(service); - if (extensions == null) { - extensions = findAllExtensionClass(service, activateName, loader); - foundFromCache = false; - providers.put(service, extensions); - } + + private static class InnerEnhancedServiceLoader { + private static final Logger LOGGER = LoggerFactory.getLogger(InnerEnhancedServiceLoader.class); + private static final String SERVICES_DIRECTORY = "META-INF/services/"; + private static final String SEATA_DIRECTORY = "META-INF/seata/"; + + private static final ConcurrentMap, InnerEnhancedServiceLoader> SERVICE_LOADERS = + new ConcurrentHashMap<>(); + + private final Class type; + private final Holder> definitionsHolder = new Holder<>(); + private final ConcurrentMap> definitionToInstanceMap = + new ConcurrentHashMap<>(); + private final ConcurrentMap> nameToDefinitionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap, ExtensionDefinition> classToDefinitionMap = new ConcurrentHashMap<>(); + + private InnerEnhancedServiceLoader(Class type) { + this.type = type; + } + + /** + * Get the ServiceLoader for the specified Class + * + * @param type the type of the extension point + * @param the type + * @return the service loader + */ + private static InnerEnhancedServiceLoader getServiceLoader(Class type) { + if (type == null) { + throw new IllegalArgumentException("Enhanced Service type == null"); + } + InnerEnhancedServiceLoader loader = (InnerEnhancedServiceLoader)SERVICE_LOADERS.get(type); + if (loader == null) { + SERVICE_LOADERS.putIfAbsent(type, new InnerEnhancedServiceLoader(type)); + loader = (InnerEnhancedServiceLoader)SERVICE_LOADERS.get(type); + } + return loader; + } + + /** + * Specify classLoader to load the service provider + * + * @param loader the loader + * @return s s + * @throws EnhancedServiceNotFoundException the enhanced service not found exception + */ + private S load(ClassLoader loader) throws EnhancedServiceNotFoundException { + return loadExtension(loader, null, null); + } + + /** + * Specify classLoader to load the service provider + * + * @param activateName the activate name + * @param loader the loader + * @return s s + * @throws EnhancedServiceNotFoundException the enhanced service not found exception + */ + private S load(String activateName, ClassLoader loader) + throws EnhancedServiceNotFoundException { + return loadExtension(activateName, loader, null, null); + } + + /** + * Load s. + * + * @param activateName the activate name + * @param args the args + * @param loader the loader + * @return the s + * @throws EnhancedServiceNotFoundException the enhanced service not found exception + */ + private S load(String activateName, Object[] args, ClassLoader loader) + throws EnhancedServiceNotFoundException { + Class[] argsType = null; + if (args != null && args.length > 0) { + argsType = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argsType[i] = args[i].getClass(); } } - if (StringUtils.isNotEmpty(activateName)) { - loadFile(service, SEATA_DIRECTORY + activateName.toLowerCase() + "/", loader, extensions); - - List activateExtensions = new ArrayList<>(); - for (Class clz : extensions) { - @SuppressWarnings("unchecked") - LoadLevel activate = (LoadLevel) clz.getAnnotation(LoadLevel.class); - if (activate != null && activateName.equalsIgnoreCase(activate.name())) { - activateExtensions.add(clz); - } + return loadExtension(activateName, loader, argsType, args); + } + + /** + * Load s. + * + * @param activateName the activate name + * @param argsType the args type + * @param args the args + * @param loader the class loader + * @return the s + * @throws EnhancedServiceNotFoundException the enhanced service not found exception + */ + private S load(String activateName, Class[] argsType, Object[] args, ClassLoader loader) + throws EnhancedServiceNotFoundException { + return loadExtension(activateName, loader, argsType, args); + } + + /** + * get all implements + * @param loader the class loader + * + * @return list list + */ + private List loadAll(ClassLoader loader) { + return loadAll(null, null, loader); + } + + /** + * get all implements + * + * @param argsType the args type + * @param args the args + * @return list list + */ + private List loadAll(Class[] argsType, Object[] args, ClassLoader loader) { + List allInstances = new ArrayList<>(); + List allClazzs = getAllExtensionClass(loader); + if (CollectionUtils.isEmpty(allClazzs)) { + return allInstances; + } + try { + for (Class clazz : allClazzs) { + ExtensionDefinition definition = classToDefinitionMap.get(clazz); + allInstances.add(getExtensionInstance(definition, loader, argsType, args)); } + } catch (Throwable t) { + throw new EnhancedServiceNotFoundException(t); + } + return allInstances; + } - extensions = activateExtensions; + /** + * Get all the extension classes, follow {@linkplain LoadLevel} defined and sort order + * + * @param loader the loader + * @return all extension class + */ + @SuppressWarnings("rawtypes") + private List getAllExtensionClass(ClassLoader loader) { + return loadAllExtensionClass(loader); + } + + @SuppressWarnings("rawtypes") + private S loadExtension(ClassLoader loader, Class[] argTypes, + Object[] args) { + try { + loadAllExtensionClass(loader); + ExtensionDefinition defaultExtensionDefinition = getDefaultExtensionDefinition(); + return getExtensionInstance(defaultExtensionDefinition, loader, argTypes, args); + } catch (Throwable e) { + if (e instanceof EnhancedServiceNotFoundException) { + throw (EnhancedServiceNotFoundException)e; + } else { + throw new EnhancedServiceNotFoundException( + "not found service provider for : " + type.getName() + " caused by " + ExceptionUtils + .getFullStackTrace(e)); + } } + } - if (extensions.isEmpty()) { - throw new EnhancedServiceNotFoundException( - "not found service provider for : " + service.getName() + "[" + activateName - + "] and classloader : " + ObjectUtils.toString(loader)); + @SuppressWarnings("rawtypes") + private S loadExtension(String activateName, ClassLoader loader, Class[] argTypes, + Object[] args) { + if (io.seata.common.util.StringUtils.isEmpty(activateName)) { + throw new IllegalArgumentException("the name of service provider for [" + type.getName() + "] name is null"); + } + try { + loadAllExtensionClass(loader); + ExtensionDefinition cachedExtensionDefinition = getCachedExtensionDefinition(activateName); + return getExtensionInstance(cachedExtensionDefinition, loader, argTypes, args); + } catch (Throwable e) { + if (e instanceof EnhancedServiceNotFoundException) { + throw (EnhancedServiceNotFoundException)e; + } else { + throw new EnhancedServiceNotFoundException( + "not found service provider for : " + type.getName() + " caused by " + ExceptionUtils + .getFullStackTrace(e)); + } } - Class extension = extensions.get(extensions.size() - 1); - S result = initInstance(service, extension, argTypes, args); - if (!foundFromCache && LOGGER.isInfoEnabled()) { - LOGGER.info("load " + service.getSimpleName() + "[" + activateName + "] extension by class[" + extension - .getName() + "]"); + } + + private S getExtensionInstance(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, + Object[] args) { + if (definition == null) { + throw new EnhancedServiceNotFoundException("not found service provider for : " + type.getName()); } - return result; - } catch (Throwable e) { - if (e instanceof EnhancedServiceNotFoundException) { - throw (EnhancedServiceNotFoundException)e; + if (Scope.SINGLETON.equals(definition.getScope())) { + Holder holder = definitionToInstanceMap.get(definition); + if (holder == null) { + definitionToInstanceMap.putIfAbsent(definition, new Holder<>()); + holder = definitionToInstanceMap.get(definition); + } + Object instance = holder.get(); + if (instance == null) { + synchronized (holder) { + instance = holder.get(); + if (instance == null) { + instance = createNewExtension(definition, loader, argTypes, args); + holder.set(instance); + } + } + } + return (S)instance; } else { - throw new EnhancedServiceNotFoundException( - "not found service provider for : " + service.getName() + " caused by " + ExceptionUtils - .getFullStackTrace(e)); + return createNewExtension(definition, loader, argTypes, args); } } - } - @SuppressWarnings("rawtypes") - private static List findAllExtensionClass(Class service, String activateName, ClassLoader loader) { - List extensions = new ArrayList<>(); - try { - loadFile(service, SERVICES_DIRECTORY, loader, extensions); - loadFile(service, SEATA_DIRECTORY, loader, extensions); - } catch (IOException e) { - throw new EnhancedServiceNotFoundException(e); + private S createNewExtension(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, Object[] args) { + Class clazz = definition.getServiceClass(); + try { + S newInstance = initInstance(clazz, argTypes, args); + return newInstance; + } catch (Throwable t) { + throw new IllegalStateException("Extension instance(definition: " + definition + ", class: " + + type + ") could not be instantiated: " + t.getMessage(), t); + } } - if (extensions.isEmpty()) { - return extensions; - } - extensions.sort((c1, c2) -> { - int o1 = 0; - int o2 = 0; - @SuppressWarnings("unchecked") - LoadLevel a1 = (LoadLevel) c1.getAnnotation(LoadLevel.class); - @SuppressWarnings("unchecked") - LoadLevel a2 = (LoadLevel) c2.getAnnotation(LoadLevel.class); - - if (a1 != null) { - o1 = a1.order(); + private List loadAllExtensionClass(ClassLoader loader) { + List result; + List definitions = definitionsHolder.get(); + if (definitions == null) { + synchronized (definitionsHolder) { + definitions = definitionsHolder.get(); + if (definitions == null) { + definitions = findAllExtensionDefinition(loader); + definitionsHolder.set(definitions); + } + } } + return definitions.stream().map(def -> def.getServiceClass()).collect(Collectors.toList()); + } - if (a2 != null) { - o2 = a2.order(); + @SuppressWarnings("rawtypes") + private List findAllExtensionDefinition(ClassLoader loader) { + List extensionDefinitions = new ArrayList<>(); + try { + loadFile(SERVICES_DIRECTORY, loader, extensionDefinitions); + loadFile(SEATA_DIRECTORY, loader, extensionDefinitions); + } catch (IOException e) { + throw new EnhancedServiceNotFoundException(e); } - return Integer.compare(o1, o2); - - }); + //After loaded all the extensions,sort the caches by order + if (!nameToDefinitionsMap.isEmpty()) { + for (List definitions : nameToDefinitionsMap.values()) { + Collections.sort(definitions, (def1, def2) -> { + int o1 = def1.getOrder(); + int o2 = def2.getOrder(); + return Integer.compare(o1, o2); + }); + } + } - return extensions; - } + if (!extensionDefinitions.isEmpty()) { + Collections.sort(extensionDefinitions, (definition1, definition2) -> { + int o1 = definition1.getOrder(); + int o2 = definition2.getOrder(); + return Integer.compare(o1, o2); + }); + } - @SuppressWarnings("rawtypes") - private static void loadFile(Class service, String dir, ClassLoader classLoader, List extensions) - throws IOException { - String fileName = dir + service.getName(); - Enumeration urls; - if (classLoader != null) { - urls = classLoader.getResources(fileName); - } else { - urls = ClassLoader.getSystemResources(fileName); + return extensionDefinitions; } - if (urls != null) { - while (urls.hasMoreElements()) { - java.net.URL url = urls.nextElement(); - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(url.openStream(), Constants.DEFAULT_CHARSET)); - String line = null; - while ((line = reader.readLine()) != null) { - final int ci = line.indexOf('#'); - if (ci >= 0) { - line = line.substring(0, ci); - } - line = line.trim(); - if (line.length() > 0) { - try { - extensions.add(Class.forName(line, true, classLoader)); - } catch (LinkageError | ClassNotFoundException e) { - LOGGER.warn("load [{}] class fail. {}", line, e.getMessage()); + + @SuppressWarnings("rawtypes") + private void loadFile(String dir, ClassLoader loader, List extensions) + throws IOException { + String fileName = dir + type.getName(); + Enumeration urls; + if (loader != null) { + urls = loader.getResources(fileName); + } else { + urls = ClassLoader.getSystemResources(fileName); + } + if (urls != null) { + while (urls.hasMoreElements()) { + java.net.URL url = urls.nextElement(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Constants.DEFAULT_CHARSET))) { + String line = null; + while ((line = reader.readLine()) != null) { + final int ci = line.indexOf('#'); + if (ci >= 0) { + line = line.substring(0, ci); + } + line = line.trim(); + if (line.length() > 0) { + try { + Class clazz = Class.forName(line, true, loader); + ExtensionDefinition extensionDefinition = getUnloadedExtensionDefinition(clazz); + if (extensionDefinition == null) { + LOGGER.warn("The same extension {} has already been loaded, skipped", line); + continue; + } + extensions.add(extensionDefinition); + } catch (LinkageError | ClassNotFoundException e) { + LOGGER.warn("Load [{}] class fail. {}", line, e.getMessage()); + } } } + } catch (Throwable e) { + LOGGER.warn(e.getMessage()); } - } catch (Throwable e) { - LOGGER.warn(e.getMessage()); - } finally { - IOUtil.close(reader); } } } - } - /** - * init instance - * - * @param the type parameter - * @param service the service - * @param implClazz the impl clazz - * @param argTypes the arg types - * @param args the args - * @return s s - * @throws IllegalAccessException the illegal access exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws InvocationTargetException the invocation target exception - */ - protected static S initInstance(Class service, Class implClazz, Class[] argTypes, Object[] args) - throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { - S s = null; - if (argTypes != null && args != null) { - // Constructor with arguments - Constructor constructor = implClazz.getDeclaredConstructor(argTypes); - s = service.cast(constructor.newInstance(args)); - } else { - // default Constructor - s = service.cast(implClazz.newInstance()); + private ExtensionDefinition getUnloadedExtensionDefinition(Class clazz) { + String serviceName = null; + Integer priority = 0; + Scope scope = Scope.SINGLETON; + LoadLevel loadLevel = clazz.getAnnotation(LoadLevel.class); + if (loadLevel != null) { + serviceName = loadLevel.name(); + priority = loadLevel.order(); + scope = loadLevel.scope(); + } + //Check whether the definition has been loaded + if (!classToDefinitionMap.containsKey(clazz)) { + ExtensionDefinition result = new ExtensionDefinition(serviceName, priority, scope, clazz); + classToDefinitionMap.put(clazz, result); + if (serviceName != null) { + nameToDefinitionsMap.computeIfAbsent(serviceName, e -> new ArrayList<>()).add(result); + } + return result; + } + return null; } - if (s instanceof Initialize) { - ((Initialize)s).init(); + + private ExtensionDefinition getDefaultExtensionDefinition() { + List currentDefinitions = definitionsHolder.get(); + if (currentDefinitions != null && currentDefinitions.size() > 0) { + return currentDefinitions.get(currentDefinitions.size() - 1); + } + return null; } - return s; - } - /** - * Cannot use TCCL, in the pandora container will cause the class in the plugin not to be loaded - * - * @return - */ - private static ClassLoader findClassLoader() { - return EnhancedServiceLoader.class.getClassLoader(); + private ExtensionDefinition getCachedExtensionDefinition(String activateName) { + if (nameToDefinitionsMap.containsKey(activateName)) { + List definitions = nameToDefinitionsMap.get(activateName); + return definitions.get(definitions.size() - 1); + } + return null; + } + + /** + * init instance + * + * @param implClazz the impl clazz + * @param argTypes the arg types + * @param args the args + * @return s s + * @throws IllegalAccessException the illegal access exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws InvocationTargetException the invocation target exception + */ + private S initInstance(Class implClazz, Class[] argTypes, Object[] args) + throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + S s = null; + if (argTypes != null && args != null) { + // Constructor with arguments + Constructor constructor = implClazz.getDeclaredConstructor(argTypes); + s = type.cast(constructor.newInstance(args)); + } else { + // default Constructor + s = type.cast(implClazz.newInstance()); + } + if (s instanceof Initialize) { + ((Initialize)s).init(); + } + return s; + } + + /** + * Helper Class for hold a value. + * @param + */ + private static class Holder { + private volatile T value; + + private void set(T value) { + this.value = value; + } + + private T get() { + return value; + } + } } -} + + +} \ No newline at end of file diff --git a/common/src/main/java/io/seata/common/loader/ExtensionDefinition.java b/common/src/main/java/io/seata/common/loader/ExtensionDefinition.java new file mode 100644 index 00000000000..2f76e53b7ff --- /dev/null +++ b/common/src/main/java/io/seata/common/loader/ExtensionDefinition.java @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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.seata.common.loader; + +import io.seata.common.util.StringUtils; + +/** + * The type ExtensionDefinition + * + * @author haozhibei + */ +final class ExtensionDefinition { + private String name; + private Class serviceClass; + private Integer order; + private Scope scope; + + public Integer getOrder() { + return this.order; + } + + public Class getServiceClass() { + return this.serviceClass; + } + + public Scope getScope() { + return this.scope; + } + + public ExtensionDefinition(String name, Integer order, Scope scope, Class clazz) { + this.name = name; + this.order = order; + this.scope = scope; + this.serviceClass = clazz; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((serviceClass == null) ? 0 : serviceClass.hashCode()); + result = prime * result + ((order == null) ? 0 : order.hashCode()); + result = prime * result + ((scope == null) ? 0 : scope.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ExtensionDefinition other = (ExtensionDefinition)obj; + if (!StringUtils.equals(name, other.name)) { + return false; + } + if (!serviceClass.equals(other.serviceClass)) { + return false; + } + if (!order.equals(other.order)) { + return false; + } + return !scope.equals(other.scope); + } + + +} diff --git a/common/src/main/java/io/seata/common/loader/LoadLevel.java b/common/src/main/java/io/seata/common/loader/LoadLevel.java index e47c4a10f17..fe49ba7569a 100644 --- a/common/src/main/java/io/seata/common/loader/LoadLevel.java +++ b/common/src/main/java/io/seata/common/loader/LoadLevel.java @@ -43,4 +43,10 @@ * @return the int */ int order() default 0; + + /** + * Scope enum. + * @return + */ + Scope scope() default Scope.SINGLETON; } diff --git a/common/src/main/java/io/seata/common/loader/Scope.java b/common/src/main/java/io/seata/common/loader/Scope.java new file mode 100644 index 00000000000..cb8d76abd34 --- /dev/null +++ b/common/src/main/java/io/seata/common/loader/Scope.java @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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.seata.common.loader; + +/** + * the scope of the extension + * + * @author haozhibei + */ +public enum Scope { + /** + * The extension will be loaded in singleton mode + */ + SINGLETON, + + /** + * The extension will be loaded in multi instance mode + */ + PROTOTYPE + +} diff --git a/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java b/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java index 9a7a0c8f794..d83726dbaac 100644 --- a/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java +++ b/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java @@ -35,7 +35,7 @@ public class EnhancedServiceLoaderTest { @Test public void testLoadByClassAndClassLoader() { Hello load = EnhancedServiceLoader.load(Hello.class, Hello.class.getClassLoader()); - Assertions.assertEquals(load.say(), "Bonjour"); + Assertions.assertEquals(load.say(), "Olá."); } /** @@ -54,7 +54,7 @@ public void testLoadException() { @Test public void testLoadByClass() { Hello load = EnhancedServiceLoader.load(Hello.class); - assertThat(load.say()).isEqualTo("Bonjour"); + assertThat(load.say()).isEqualTo("Olá."); } /** @@ -82,10 +82,10 @@ public void testLoadByClassAndClassLoaderAndActivateName() { @Test public void getAllExtensionClass() { List allExtensionClass = EnhancedServiceLoader.getAllExtensionClass(Hello.class); + assertThat(allExtensionClass.get(3).getSimpleName()).isEqualTo((LatinHello.class.getSimpleName())); assertThat(allExtensionClass.get(2).getSimpleName()).isEqualTo((FrenchHello.class.getSimpleName())); assertThat(allExtensionClass.get(1).getSimpleName()).isEqualTo((EnglishHello.class.getSimpleName())); assertThat(allExtensionClass.get(0).getSimpleName()).isEqualTo((ChineseHello.class.getSimpleName())); - } /** @@ -98,4 +98,32 @@ public void getAllExtensionClass1() { assertThat(allExtensionClass).isNotEmpty(); } + @Test + public void getSingletonExtensionInstance(){ + Hello hello1 = EnhancedServiceLoader.load(Hello.class, "ChineseHello"); + Hello hello2 = EnhancedServiceLoader.load(Hello.class, "ChineseHello"); + assertThat(hello1 == hello2).isTrue(); + } + + @Test + public void getMultipleExtensionInstance(){ + Hello hello1 = EnhancedServiceLoader.load(Hello.class, "LatinHello"); + Hello hello2 = EnhancedServiceLoader.load(Hello.class, "LatinHello"); + assertThat(hello1 == hello2).isFalse(); + } + + @Test + public void getAllInstances(){ + List hellows1 = EnhancedServiceLoader.loadAll(Hello.class); + List hellows2 = EnhancedServiceLoader.loadAll(Hello.class); + for (Hello hello : hellows1){ + if(hello.say()!="Olá."){ + assertThat(hellows2.contains(hello)).isTrue(); + } + else{ + assertThat(hellows2.contains(hello)).isFalse(); + } + } + } + } diff --git a/common/src/test/java/io/seata/common/loader/LatinHello.java b/common/src/test/java/io/seata/common/loader/LatinHello.java new file mode 100644 index 00000000000..458b10cab1b --- /dev/null +++ b/common/src/test/java/io/seata/common/loader/LatinHello.java @@ -0,0 +1,29 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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.seata.common.loader; + +/** + * The type LatinHello + * + * @author haozhibei + */ +@LoadLevel(name = "LatinHello",order = 3, scope = Scope.PROTOTYPE) +public class LatinHello implements Hello { + @Override + public String say() { + return "Olá."; + } +} diff --git a/common/src/test/resources/META-INF/seata/frenchhello/io.seata.common.loader.Hello b/common/src/test/resources/META-INF/seata/frenchhello/io.seata.common.loader.Hello index 6b236a07b48..76d8cfffd8c 100644 --- a/common/src/test/resources/META-INF/seata/frenchhello/io.seata.common.loader.Hello +++ b/common/src/test/resources/META-INF/seata/frenchhello/io.seata.common.loader.Hello @@ -15,4 +15,5 @@ # -io.seata.common.loader.EnglishHello \ No newline at end of file +io.seata.common.loader.EnglishHello +io.seata.common.loader.LatinHello \ No newline at end of file diff --git a/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello b/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello index c3ebe7adaa3..81f68fa6702 100644 --- a/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello +++ b/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello @@ -15,4 +15,5 @@ # io.seata.common.loader.FrenchHello -io.seata.common.loader.EnglishHello \ No newline at end of file +io.seata.common.loader.EnglishHello +io.seata.common.loader.LatinHello \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/store/StoreMode.java b/core/src/main/java/io/seata/core/store/StoreMode.java index 666cfc51303..6fe55967656 100644 --- a/core/src/main/java/io/seata/core/store/StoreMode.java +++ b/core/src/main/java/io/seata/core/store/StoreMode.java @@ -25,26 +25,35 @@ public enum StoreMode { /** * file store */ - FILE, + FILE("file"), /** * database store */ - DB; + DB("db"); + + private String name; + + StoreMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } /** - * Valueof store mode. - * - * @param mode the mode + * get value of store mode + * @param name the mode name * @return the store mode */ - public static StoreMode valueof(String mode) { - for (StoreMode sm : values()) { - if (sm.name().equalsIgnoreCase(mode)) { + public static StoreMode get(String name) { + for (StoreMode sm : StoreMode.class.getEnumConstants()) { + if (sm.name.equalsIgnoreCase(name)) { return sm; } } - throw new IllegalArgumentException("unknown store mode:" + mode); + throw new IllegalArgumentException("unknown store mode:" + name); } } diff --git a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterFactory.java b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterFactory.java index e2eea293f7a..e21a0dd1a08 100644 --- a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterFactory.java +++ b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterFactory.java @@ -45,7 +45,7 @@ public static List getInstanceList() { try { exporterType = ExporterType.getType(exporterTypeName); exporters.add( - EnhancedServiceLoader.load(Exporter.class, Objects.requireNonNull(exporterType).name())); + EnhancedServiceLoader.load(Exporter.class, Objects.requireNonNull(exporterType).getName())); } catch (Exception exx) { LOGGER.error("not support metrics exporter type: {}",exporterTypeName, exx); } diff --git a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterType.java b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterType.java index a0d45a960ba..5342149902e 100644 --- a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterType.java +++ b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/exporter/ExporterType.java @@ -26,7 +26,17 @@ public enum ExporterType { /** * Export metrics data to Prometheus */ - PROMETHEUS; + PROMETHEUS("prometheus"); + + private String name; + + ExporterType(String name) { + this.name = name; + } + + public String getName() { + return name; + } public static ExporterType getType(String name) { if (PROMETHEUS.name().equalsIgnoreCase(name)) { diff --git a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryFactory.java b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryFactory.java index 2123eee9dc1..e864eb4867c 100644 --- a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryFactory.java +++ b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryFactory.java @@ -39,7 +39,7 @@ public static Registry getInstance() { } catch (Exception exx) { throw new NotSupportYetException("not support metrics registry type: " + registryTypeName); } - return EnhancedServiceLoader.load(Registry.class, Objects.requireNonNull(registryType).name()); + return EnhancedServiceLoader.load(Registry.class, Objects.requireNonNull(registryType).getName()); } return null; } diff --git a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryType.java b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryType.java index cd435d97e7d..5b15d4a4204 100644 --- a/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryType.java +++ b/metrics/seata-metrics-core/src/main/java/io/seata/metrics/registry/RegistryType.java @@ -26,7 +26,17 @@ public enum RegistryType { /** * Built-in compact metrics registry */ - COMPACT; + COMPACT("compact"); + + private String name; + + RegistryType(String name) { + this.name = name; + } + + public String getName() { + return name; + } public static RegistryType getType(String name) { if (COMPACT.name().equalsIgnoreCase(name)) { diff --git a/metrics/seata-metrics-exporter-prometheus/src/main/java/io/seata/metrics/exporter/prometheus/PrometheusExporter.java b/metrics/seata-metrics-exporter-prometheus/src/main/java/io/seata/metrics/exporter/prometheus/PrometheusExporter.java index 7e2c87531cd..36ffbbd1c51 100644 --- a/metrics/seata-metrics-exporter-prometheus/src/main/java/io/seata/metrics/exporter/prometheus/PrometheusExporter.java +++ b/metrics/seata-metrics-exporter-prometheus/src/main/java/io/seata/metrics/exporter/prometheus/PrometheusExporter.java @@ -37,7 +37,7 @@ * * @author zhengyangyong */ -@LoadLevel(name = "Prometheus", order = 1) +@LoadLevel(name = "prometheus", order = 1) public class PrometheusExporter extends Collector implements Collector.Describable, Exporter { private final HTTPServer server; diff --git a/metrics/seata-metrics-registry-compact/src/main/java/io/seata/metrics/registry/compact/CompactRegistry.java b/metrics/seata-metrics-registry-compact/src/main/java/io/seata/metrics/registry/compact/CompactRegistry.java index 0fc4d7dc588..c9e4095ca16 100644 --- a/metrics/seata-metrics-registry-compact/src/main/java/io/seata/metrics/registry/compact/CompactRegistry.java +++ b/metrics/seata-metrics-registry-compact/src/main/java/io/seata/metrics/registry/compact/CompactRegistry.java @@ -37,7 +37,7 @@ * * @author zhengyangyong */ -@LoadLevel(name = "Compact", order = 1) +@LoadLevel(name = "compact", order = 1) public class CompactRegistry implements Registry { private static final Map METERS = new ConcurrentHashMap<>(); diff --git a/server/src/main/java/io/seata/server/session/SessionHolder.java b/server/src/main/java/io/seata/server/session/SessionHolder.java index 32134473662..fbc5834a044 100644 --- a/server/src/main/java/io/seata/server/session/SessionHolder.java +++ b/server/src/main/java/io/seata/server/session/SessionHolder.java @@ -84,15 +84,15 @@ public static void init(String mode) throws IOException { mode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE); } //the store mode - StoreMode storeMode = StoreMode.valueof(mode); + StoreMode storeMode = StoreMode.get(mode); if (StoreMode.DB.equals(storeMode)) { //database store - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name()); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name(), + ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName()); + ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), new Object[] {ASYNC_COMMITTING_SESSION_MANAGER_NAME}); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name(), + RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), new Object[] {RETRY_COMMITTING_SESSION_MANAGER_NAME}); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name(), + RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), new Object[] {RETRY_ROLLBACKING_SESSION_MANAGER_NAME}); } else if (StoreMode.FILE.equals(storeMode)) { //file store @@ -101,13 +101,13 @@ public static void init(String mode) throws IOException { if (sessionStorePath == null) { throw new StoreException("the {store.file.dir} is empty."); } - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.name(), + ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(), new Object[] {ROOT_SESSION_MANAGER_NAME, sessionStorePath}); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.name(), + ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(), new Class[] {String.class, String.class}, new Object[] {ASYNC_COMMITTING_SESSION_MANAGER_NAME, null}); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.name(), + RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(), new Class[] {String.class, String.class}, new Object[] {RETRY_COMMITTING_SESSION_MANAGER_NAME, null}); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.name(), + RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(), new Class[] {String.class, String.class}, new Object[] {RETRY_ROLLBACKING_SESSION_MANAGER_NAME, null}); } else { //unknown store diff --git a/server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java b/server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java index cf40d92cfe2..3d1c15e0222 100644 --- a/server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java +++ b/server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java @@ -36,6 +36,7 @@ import io.seata.server.session.SessionManager; import io.seata.server.storage.db.store.DataBaseTransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; +import io.seata.common.loader.Scope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +45,7 @@ * * @author zhangsen */ -@LoadLevel(name = "db") +@LoadLevel(name = "db", scope = Scope.PROTOTYPE) public class DataBaseSessionManager extends AbstractSessionManager implements SessionManager, SessionLifecycleListener, Initialize, Reloadable { diff --git a/server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java b/server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java index a7c972bc62f..9c075090caf 100644 --- a/server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java +++ b/server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java @@ -44,13 +44,15 @@ import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; +import io.seata.common.loader.Scope; + /** * The type File based session manager. * * @author slievrly */ -@LoadLevel(name = "file") +@LoadLevel(name = "file", scope = Scope.PROTOTYPE) public class FileSessionManager extends AbstractSessionManager implements Reloadable { private static final int READ_SIZE = ConfigurationFactory.getInstance().getInt( diff --git a/server/src/test/java/io/seata/server/session/SessionHolderTest.java b/server/src/test/java/io/seata/server/session/SessionHolderTest.java index 8d27935c890..d5ed5144c2b 100644 --- a/server/src/test/java/io/seata/server/session/SessionHolderTest.java +++ b/server/src/test/java/io/seata/server/session/SessionHolderTest.java @@ -48,7 +48,7 @@ public void testInit() throws IOException { if (rootSessionFile.exists()) { rootSessionFile.delete(); } - final String mode = StoreMode.FILE.toString(); + final String mode = StoreMode.FILE.getName(); SessionHolder.init(mode); try { final File actual = new File(pathname); diff --git a/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/DruidIsolationTest.java b/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/DruidIsolationTest.java index 23c2d38b3d0..ac7c5c729a7 100644 --- a/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/DruidIsolationTest.java +++ b/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/DruidIsolationTest.java @@ -20,6 +20,7 @@ import io.seata.sqlparser.SQLRecognizerFactory; import io.seata.sqlparser.SqlParserType; import io.seata.sqlparser.util.JdbcConstants; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -40,4 +41,11 @@ public void testDruidIsolation() throws Exception { // because druid-test.jar not exists, so NoClassDefFoundError should be threw Assertions.assertThrows(NoClassDefFoundError.class, () -> recognizerFactory.create(TEST_SQL, JdbcConstants.MYSQL)); } + + @AfterAll + public static void afterClass(){ + DruidDelegatingSQLRecognizerFactory recognizerFactory = (DruidDelegatingSQLRecognizerFactory) EnhancedServiceLoader.load(SQLRecognizerFactory.class, + SqlParserType.SQL_PARSER_TYPE_DRUID); + recognizerFactory.setClassLoader(DruidIsolationTest.class.getClassLoader()); + } }