This is an automated email from the ASF dual-hosted git repository. mikexue pushed a commit to branch 1.3.0 in repository https://gitbox.apache.org/repos/asf/eventmesh.git
commit 348dc3cf1e802c6195353472a9540599bfa843af Author: Wenjun Ruan <[email protected]> AuthorDate: Wed Jul 7 16:57:02 2021 +0800 [ISSUE #367]Enhance SPI plugins (#419) --- eventmesh-common/gradle.properties | 2 +- settings.gradle => eventmesh-spi/build.gradle | 7 +- .../gradle.properties | 3 +- .../eventmesh/spi/EventMeshExtensionFactory.java | 22 ++++- .../eventmesh/spi/EventMeshExtensionLoader.java | 110 +++++++++++++++++++++ .../org/apache/eventmesh/spi/EventMeshSPI.java | 20 +++- .../apache/eventmesh/spi/ExtensionException.java | 18 +++- .../spi/EventMeshExtensionFactoryTest.java | 14 ++- .../java/org/apache/eventmesh/spi/ExtensionA.java | 11 ++- .../org/apache/eventmesh/spi/TestExtension.java | 9 +- .../org.apache.eventmesh.spi.TestExtension | 6 +- settings.gradle | 9 +- 12 files changed, 200 insertions(+), 31 deletions(-) diff --git a/eventmesh-common/gradle.properties b/eventmesh-common/gradle.properties index b4202ce82..4117dffc9 100644 --- a/eventmesh-common/gradle.properties +++ b/eventmesh-common/gradle.properties @@ -16,4 +16,4 @@ # group=org.apache.eventmesh version=1.2.0-SNAPSHOT -jdk=1.7 +jdk=1.8 diff --git a/settings.gradle b/eventmesh-spi/build.gradle similarity index 77% copy from settings.gradle copy to eventmesh-spi/build.gradle index ea31ffdc1..d973dceda 100644 --- a/settings.gradle +++ b/eventmesh-spi/build.gradle @@ -13,9 +13,4 @@ * 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. - */ - -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' - + */ \ No newline at end of file diff --git a/eventmesh-common/gradle.properties b/eventmesh-spi/gradle.properties similarity index 97% copy from eventmesh-common/gradle.properties copy to eventmesh-spi/gradle.properties index b4202ce82..d0503c3b7 100644 --- a/eventmesh-common/gradle.properties +++ b/eventmesh-spi/gradle.properties @@ -16,4 +16,5 @@ # group=org.apache.eventmesh version=1.2.0-SNAPSHOT -jdk=1.7 +jdk=1.8 +snapshot=false diff --git a/settings.gradle b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java similarity index 51% copy from settings.gradle copy to eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java index ea31ffdc1..96e054bd0 100644 --- a/settings.gradle +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java @@ -15,7 +15,23 @@ * limitations under the License. */ -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +package org.apache.eventmesh.spi; +import org.apache.commons.lang3.StringUtils; + +public enum EventMeshExtensionFactory { + ; + + public static <T> T getExtension(Class<T> extensionType, String extensionName) { + if (extensionType == null) { + throw new ExtensionException("extensionType is null"); + } + if (StringUtils.isEmpty(extensionName)) { + throw new ExtensionException("extensionName is null"); + } + if (!extensionType.isInterface() || !extensionType.isAnnotationPresent(EventMeshSPI.class)) { + throw new ExtensionException(String.format("extensionType:%s is invalided", extensionType)); + } + return EventMeshExtensionLoader.getExtension(extensionType, extensionName); + } +} diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionLoader.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionLoader.java new file mode 100644 index 000000000..740ecb3f9 --- /dev/null +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionLoader.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.eventmesh.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +public enum EventMeshExtensionLoader { + ; + + private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Class<?>>> EXTENSION_CLASS_LOAD_CACHE = new ConcurrentHashMap<>(16); + + private static final ConcurrentHashMap<String, Object> EXTENSION_INSTANCE_CACHE = new ConcurrentHashMap<>(16); + + private static final String EVENTMESH_EXTENSION_DIR = "META-INF/eventmesh/"; + + @SuppressWarnings("unchecked") + public static <T> T getExtension(Class<T> extensionType, String extensionName) { + if (!hasLoadExtensionClass(extensionType)) { + loadExtensionClass(extensionType); + } + if (!hasInitializeExtension(extensionName)) { + initializeExtension(extensionType, extensionName); + } + return (T) EXTENSION_INSTANCE_CACHE.get(extensionName); + } + + private static <T> void initializeExtension(Class<T> extensionType, String extensionName) { + ConcurrentHashMap<String, Class<?>> extensionClassMap = EXTENSION_CLASS_LOAD_CACHE.get(extensionType); + if (extensionClassMap == null) { + throw new ExtensionException(String.format("Extension type:%s has not been loaded", extensionType)); + } + if (!extensionClassMap.containsKey(extensionName)) { + throw new ExtensionException(String.format("Extension name:%s has not been loaded", extensionName)); + } + Class<?> aClass = extensionClassMap.get(extensionName); + try { + EXTENSION_INSTANCE_CACHE.put(extensionName, aClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + throw new ExtensionException("Extension initialize error", e); + } + } + + public static <T> void loadExtensionClass(Class<T> extensionType) { + String extensionFileName = EVENTMESH_EXTENSION_DIR + extensionType.getName(); + ClassLoader classLoader = EventMeshExtensionLoader.class.getClassLoader(); + try { + Enumeration<URL> extensionUrls = classLoader.getResources(extensionFileName); + if (extensionUrls != null) { + while (extensionUrls.hasMoreElements()) { + URL url = extensionUrls.nextElement(); + loadResources(url, extensionType); + } + } + } catch (IOException e) { + throw new ExtensionException("load extension class error", e); + } + + + } + + private static <T> void loadResources(URL url, Class<T> extensionType) throws IOException { + try (InputStream inputStream = url.openStream()) { + Properties properties = new Properties(); + properties.load(inputStream); + properties.forEach((extensionName, extensionClass) -> { + String extensionNameStr = (String) extensionName; + String extensionClassStr = (String) extensionClass; + try { + Class<?> targetClass = Class.forName(extensionClassStr); + if (!extensionType.isAssignableFrom(targetClass)) { + throw new ExtensionException( + String.format("class: %s is not subClass of %s", targetClass, extensionType)); + } + EXTENSION_CLASS_LOAD_CACHE.computeIfAbsent(extensionType, k -> new ConcurrentHashMap<>()) + .put(extensionNameStr, targetClass); + } catch (ClassNotFoundException e) { + throw new ExtensionException("load extension class error", e); + } + }); + } + } + + private static <T> boolean hasLoadExtensionClass(Class<T> extensionType) { + return EXTENSION_CLASS_LOAD_CACHE.containsKey(extensionType); + } + + private static boolean hasInitializeExtension(String extensionName) { + return EXTENSION_INSTANCE_CACHE.containsKey(extensionName); + } +} diff --git a/settings.gradle b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java similarity index 67% copy from settings.gradle copy to eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java index ea31ffdc1..0ea72d431 100644 --- a/settings.gradle +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java @@ -15,7 +15,21 @@ * limitations under the License. */ -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +package org.apache.eventmesh.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Just as a marker for SPI + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface EventMeshSPI { + +} diff --git a/settings.gradle b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/ExtensionException.java similarity index 70% copy from settings.gradle copy to eventmesh-spi/src/main/java/org/apache/eventmesh/spi/ExtensionException.java index ea31ffdc1..874f03da5 100644 --- a/settings.gradle +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/ExtensionException.java @@ -15,7 +15,19 @@ * limitations under the License. */ -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +package org.apache.eventmesh.spi; +public class ExtensionException extends RuntimeException { + + public ExtensionException(Exception e) { + super(e); + } + + public ExtensionException(String message) { + super(message); + } + + public ExtensionException(String message, Exception e) { + super(message, e); + } +} diff --git a/settings.gradle b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/EventMeshExtensionFactoryTest.java similarity index 73% copy from settings.gradle copy to eventmesh-spi/src/test/java/org/apache/eventmesh/spi/EventMeshExtensionFactoryTest.java index ea31ffdc1..649f4b18b 100644 --- a/settings.gradle +++ b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/EventMeshExtensionFactoryTest.java @@ -15,7 +15,15 @@ * limitations under the License. */ -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +package org.apache.eventmesh.spi; +import org.junit.Test; + +public class EventMeshExtensionFactoryTest { + + @Test + public void getExtension() { + TestExtension extensionA = EventMeshExtensionFactory.getExtension(TestExtension.class, "extensionA"); + extensionA.hello(); + } +} \ No newline at end of file diff --git a/settings.gradle b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/ExtensionA.java similarity index 78% copy from settings.gradle copy to eventmesh-spi/src/test/java/org/apache/eventmesh/spi/ExtensionA.java index ea31ffdc1..03513e620 100644 --- a/settings.gradle +++ b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/ExtensionA.java @@ -15,7 +15,12 @@ * limitations under the License. */ -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +package org.apache.eventmesh.spi; +public class ExtensionA implements TestExtension { + + @Override + public void hello() { + System.out.println("I am ExtensionA"); + } +} diff --git a/settings.gradle b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/TestExtension.java similarity index 78% copy from settings.gradle copy to eventmesh-spi/src/test/java/org/apache/eventmesh/spi/TestExtension.java index ea31ffdc1..c0c988813 100644 --- a/settings.gradle +++ b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/TestExtension.java @@ -15,7 +15,10 @@ * limitations under the License. */ -rootProject.name = 'EventMesh' -String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +package org.apache.eventmesh.spi; +@EventMeshSPI +public interface TestExtension { + + void hello(); +} diff --git a/eventmesh-common/gradle.properties b/eventmesh-spi/src/test/resources/META-INF/eventmesh/org.apache.eventmesh.spi.TestExtension similarity index 92% copy from eventmesh-common/gradle.properties copy to eventmesh-spi/src/test/resources/META-INF/eventmesh/org.apache.eventmesh.spi.TestExtension index b4202ce82..3862ccbb4 100644 --- a/eventmesh-common/gradle.properties +++ b/eventmesh-spi/src/test/resources/META-INF/eventmesh/org.apache.eventmesh.spi.TestExtension @@ -13,7 +13,5 @@ # 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. -# -group=org.apache.eventmesh -version=1.2.0-SNAPSHOT -jdk=1.7 + +extensionA=org.apache.eventmesh.spi.ExtensionA \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index ea31ffdc1..2b5e0af66 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,5 +17,12 @@ rootProject.name = 'EventMesh' String jdkVersion = "${jdk}" -include 'eventmesh-runtime','eventmesh-connector-rocketmq','eventmesh-sdk-java','eventmesh-common','eventmesh-connector-api','eventmesh-starter','eventmesh-test' +include 'eventmesh-runtime' +include 'eventmesh-connector-rocketmq' +include 'eventmesh-sdk-java' +include 'eventmesh-common' +include 'eventmesh-connector-api' +include 'eventmesh-starter' +include 'eventmesh-test' +include 'eventmesh-spi' --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
