From 154f4d7a85b535b954eb0b868088c42deb5ffd50 Mon Sep 17 00:00:00 2001 From: Wenjun Ruan Date: Sun, 3 Oct 2021 22:24:19 +0800 Subject: [PATCH] [ISSUE #367] Optimize plugin load (#519) * 1. Optimize plugin load, support load plugin by plugin instance name 2. Optimize install plugin, suppory install plugin by ./gradlew jar dist 3. Make UrlClassLoader siglenton * add license header * optimize path * resolve confilct --- .gitignore | 3 +- build.gradle | 39 +++++++++++++ eventmesh-connector-plugin/build.gradle | 24 +------- .../connector/ConnectorResourceService.java | 3 +- .../api/consumer/MeshMQPushConsumer.java | 3 +- .../api/producer/MeshMQProducer.java | 3 +- .../consumer/RocketMQConsumerImpl.java | 9 +-- .../producer/RocketMQProducerImpl.java | 6 +- eventmesh-registry-plugin/build.gradle | 21 ------- .../api/registry/RegistryService.java | 3 +- eventmesh-security-plugin/build.gradle | 23 +------- .../apache/eventmesh/api/acl/AclService.java | 3 +- .../spi/EventMeshExtensionFactory.java | 22 ++++---- .../eventmesh/spi/EventMeshExtensionType.java | 40 +++++++++++++ .../apache/eventmesh/spi/EventMeshSPI.java | 6 ++ .../spi/loader/EventMeshUrlClassLoader.java | 56 +++++++++++++++++++ .../spi/loader/ExtensionClassLoader.java | 9 +-- .../spi/loader/JarExtensionClassLoader.java | 32 ++++++----- .../loader/MetaInfExtensionClassLoader.java | 4 +- .../spi/example/TestPrototypeExtension.java | 3 +- .../spi/example/TestSingletonExtension.java | 3 +- 21 files changed, 200 insertions(+), 115 deletions(-) create mode 100644 eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionType.java create mode 100644 eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/EventMeshUrlClassLoader.java diff --git a/.gitignore b/.gitignore index 2f470f5752..922026cd64 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ dist classes package-lock.json node_modules -.DS_Store \ No newline at end of file +.DS_Store +.run \ No newline at end of file diff --git a/build.gradle b/build.gradle index ae7f4280c8..bc7d881642 100644 --- a/build.gradle +++ b/build.gradle @@ -187,6 +187,22 @@ subprojects { } } + // pluginType: + // pluginInstanceName: + // moduleName + Map> pluginTypeMap = [ + "connector": [ + "rocketmq" : "eventmesh-connector-rocketmq", + "standalone": "eventmesh-connector-standalone", + ], + "security" : [ + "acl": "eventmesh-security-acl", + ], + "registry" : [ + "namesrv": "eventmesh-registry-rocketmq-namesrv", + ] + ] + task dist(dependsOn: ['jar']) { doFirst { new File(projectDir, '../dist/bin').mkdirs() @@ -196,6 +212,29 @@ subprojects { } doLast { + pluginTypeMap.forEach((pluginType, pluginInstanceMap) -> { + pluginInstanceMap.forEach((pluginInstanceName, moduleName) -> { + if (moduleName == project.name) { + println String.format("install plugin, pluginType: %s, pluginInstanceName: %s, module: %s", + pluginType, pluginInstanceName, moduleName) + new File("${rootDir}/dist/plugin/${pluginType}/${pluginInstanceName}").mkdirs() + copy { + into "${rootDir}/dist/plugin/${pluginType}/${pluginInstanceName}" + from project.jar.getArchivePath() + } + copy { + into "${rootDir}/dist/plugin/${pluginType}/${pluginInstanceName}" + from project.configurations.runtimeClasspath + } + copy { + into "${rootDir}/dist/conf" + from sourceSets.main.resources.srcDirs + exclude 'META-INF' + } + } + }) + }) + copy { into('../dist/apps/') from project.jar.getArchivePath() diff --git a/eventmesh-connector-plugin/build.gradle b/eventmesh-connector-plugin/build.gradle index 3c4555bec3..d973dcedae 100644 --- a/eventmesh-connector-plugin/build.gradle +++ b/eventmesh-connector-plugin/build.gradle @@ -13,26 +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. - */ - -task copyConnectorPlugin(dependsOn: ['jar']) { - doFirst { - new File(projectDir, '../eventmesh-connector-plugin/dist/apps').mkdir() - new File(projectDir, '../dist/plugin/connector').mkdirs() - } - doLast { - copy { - into('../eventmesh-connector-plugin/dist/apps/') - from project.jar.getArchivePath() - exclude { - "eventmesh-connector-plugin-${version}.jar" - "eventmesh-connector-api-${version}.jar" - } - } - copy { - into '../dist/plugin/connector' - from "../eventmesh-connector-plugin/dist/apps/eventmesh-connector-standalone-${version}.jar" - from "../eventmesh-connector-plugin/dist/apps/eventmesh-connector-rocketmq-${version}.jar" - } - } -} \ No newline at end of file + */ \ No newline at end of file diff --git a/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/connector/ConnectorResourceService.java b/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/connector/ConnectorResourceService.java index d48f8cede2..d701639ae9 100644 --- a/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/connector/ConnectorResourceService.java +++ b/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/connector/ConnectorResourceService.java @@ -16,9 +16,10 @@ */ package org.apache.eventmesh.api.connector; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; -@EventMeshSPI(isSingleton = true) +@EventMeshSPI(isSingleton = true, eventMeshExtensionType = EventMeshExtensionType.CONNECTOR) public interface ConnectorResourceService { /** diff --git a/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/consumer/MeshMQPushConsumer.java b/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/consumer/MeshMQPushConsumer.java index 998fcc1f08..dcf558b685 100644 --- a/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/consumer/MeshMQPushConsumer.java +++ b/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/consumer/MeshMQPushConsumer.java @@ -25,9 +25,10 @@ import io.openmessaging.api.Message; import org.apache.eventmesh.api.AbstractContext; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; -@EventMeshSPI(isSingleton = false) +@EventMeshSPI(isSingleton = false, eventMeshExtensionType = EventMeshExtensionType.CONNECTOR) public interface MeshMQPushConsumer extends Consumer { void init(Properties keyValue) throws Exception; diff --git a/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/producer/MeshMQProducer.java b/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/producer/MeshMQProducer.java index ef0d2f5d72..d87be7d842 100644 --- a/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/producer/MeshMQProducer.java +++ b/eventmesh-connector-plugin/eventmesh-connector-api/src/main/java/org/apache/eventmesh/api/producer/MeshMQProducer.java @@ -24,9 +24,10 @@ import io.openmessaging.api.SendCallback; import org.apache.eventmesh.api.RRCallback; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; -@EventMeshSPI(isSingleton = false) +@EventMeshSPI(isSingleton = false, eventMeshExtensionType = EventMeshExtensionType.CONNECTOR) public interface MeshMQProducer extends Producer { void init(Properties properties) throws Exception; diff --git a/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/consumer/RocketMQConsumerImpl.java b/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/consumer/RocketMQConsumerImpl.java index 708773910a..d885af0874 100644 --- a/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/consumer/RocketMQConsumerImpl.java +++ b/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/consumer/RocketMQConsumerImpl.java @@ -29,11 +29,10 @@ import io.openmessaging.api.MessageListener; import io.openmessaging.api.MessageSelector; import io.openmessaging.api.MessagingAccessPoint; -import io.openmessaging.api.OMS; -import io.openmessaging.api.OMSBuiltinKeys; import org.apache.eventmesh.api.AbstractContext; import org.apache.eventmesh.api.consumer.MeshMQPushConsumer; +import org.apache.eventmesh.connector.rocketmq.MessagingAccessPointImpl; import org.apache.eventmesh.connector.rocketmq.common.Constants; import org.apache.eventmesh.connector.rocketmq.common.EventMeshConstants; import org.apache.eventmesh.connector.rocketmq.config.ClientConfiguration; @@ -53,8 +52,6 @@ public class RocketMQConsumerImpl implements MeshMQPushConsumer { public Logger messageLogger = LoggerFactory.getLogger("message"); - public final String DEFAULT_ACCESS_DRIVER = "org.apache.eventmesh.connector.rocketmq.MessagingAccessPointImpl"; - private PushConsumerImpl pushConsumer; @Override @@ -75,9 +72,7 @@ public synchronized void init(Properties keyValue) throws Exception { } String omsNamesrv = clientConfiguration.namesrvAddr; -// KeyValue properties = OMS.newKeyValue().put(OMSBuiltinKeys.DRIVER_IMPL, DEFAULT_ACCESS_DRIVER); Properties properties = new Properties(); - properties.put(OMSBuiltinKeys.DRIVER_IMPL, DEFAULT_ACCESS_DRIVER); properties.put("ACCESS_POINTS", omsNamesrv); properties.put("REGION", "namespace"); properties.put("instanceName", instanceName); @@ -87,7 +82,7 @@ public synchronized void init(Properties keyValue) throws Exception { } else { properties.put("MESSAGE_MODEL", MessageModel.CLUSTERING.name()); } - MessagingAccessPoint messagingAccessPoint = OMS.builder().build(properties); + MessagingAccessPoint messagingAccessPoint = new MessagingAccessPointImpl(properties); pushConsumer = (PushConsumerImpl) messagingAccessPoint.createConsumer(properties); } diff --git a/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/producer/RocketMQProducerImpl.java b/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/producer/RocketMQProducerImpl.java index eba520881f..75e2360ba1 100644 --- a/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/producer/RocketMQProducerImpl.java +++ b/eventmesh-connector-plugin/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/producer/RocketMQProducerImpl.java @@ -31,6 +31,7 @@ import org.apache.eventmesh.api.RRCallback; import org.apache.eventmesh.api.producer.MeshMQProducer; +import org.apache.eventmesh.connector.rocketmq.MessagingAccessPointImpl; import org.apache.eventmesh.connector.rocketmq.common.EventMeshConstants; import org.apache.eventmesh.connector.rocketmq.config.ClientConfiguration; import org.apache.eventmesh.connector.rocketmq.config.ConfigurationWrapper; @@ -48,8 +49,6 @@ public class RocketMQProducerImpl implements MeshMQProducer { private ProducerImpl producer; - public final String DEFAULT_ACCESS_DRIVER = "org.apache.eventmesh.connector.rocketmq.MessagingAccessPointImpl"; - @Override public synchronized void init(Properties keyValue) { ConfigurationWrapper configurationWrapper = @@ -62,14 +61,13 @@ public synchronized void init(Properties keyValue) { String omsNamesrv = clientConfiguration.namesrvAddr; Properties properties = new Properties(); - properties.put(OMSBuiltinKeys.DRIVER_IMPL, DEFAULT_ACCESS_DRIVER); properties.put("ACCESS_POINTS", omsNamesrv); properties.put("REGION", "namespace"); properties.put("RMQ_PRODUCER_GROUP", producerGroup); properties.put("OPERATION_TIMEOUT", 3000); properties.put("PRODUCER_ID", producerGroup); - MessagingAccessPoint messagingAccessPoint = OMS.builder().build(properties); + MessagingAccessPoint messagingAccessPoint = new MessagingAccessPointImpl(properties); producer = (ProducerImpl) messagingAccessPoint.createProducer(properties); } diff --git a/eventmesh-registry-plugin/build.gradle b/eventmesh-registry-plugin/build.gradle index 87e4a9bb1b..2944f98194 100644 --- a/eventmesh-registry-plugin/build.gradle +++ b/eventmesh-registry-plugin/build.gradle @@ -14,24 +14,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -task copyRegistryPlugin(dependsOn: ['jar']) { - doFirst { - new File(projectDir, '../eventmesh-registry-plugin/dist/apps').mkdir() - new File(projectDir, '../dist/plugin/registry').mkdirs() - } - doLast { - copy { - into('../eventmesh-registry-plugin/dist/apps/') - from project.jar.getArchivePath() - exclude { - "eventmesh-registry-plugin-${version}.jar" - "eventmesh-registry-api-${version}.jar" - } - } - copy { - into '../dist/plugin/registry' - from "../eventmesh-registry-plugin/dist/apps/eventmesh-registry-rocketmq-namesrv-${version}.jar" - } - } -} \ No newline at end of file diff --git a/eventmesh-registry-plugin/eventmesh-registry-api/src/main/java/org/apache/eventmesh/api/registry/RegistryService.java b/eventmesh-registry-plugin/eventmesh-registry-api/src/main/java/org/apache/eventmesh/api/registry/RegistryService.java index e161b7188b..5363b2d8b0 100644 --- a/eventmesh-registry-plugin/eventmesh-registry-api/src/main/java/org/apache/eventmesh/api/registry/RegistryService.java +++ b/eventmesh-registry-plugin/eventmesh-registry-api/src/main/java/org/apache/eventmesh/api/registry/RegistryService.java @@ -20,12 +20,13 @@ import org.apache.eventmesh.api.registry.dto.EventMeshDataInfo; import org.apache.eventmesh.api.registry.dto.EventMeshRegisterInfo; import org.apache.eventmesh.api.registry.dto.EventMeshUnRegisterInfo; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; import java.util.List; import java.util.Map; -@EventMeshSPI(isSingleton = true) +@EventMeshSPI(isSingleton = true, eventMeshExtensionType = EventMeshExtensionType.REGISTRY) public interface RegistryService { void init() throws RegistryException; diff --git a/eventmesh-security-plugin/build.gradle b/eventmesh-security-plugin/build.gradle index dd414e305b..d973dcedae 100644 --- a/eventmesh-security-plugin/build.gradle +++ b/eventmesh-security-plugin/build.gradle @@ -13,25 +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. - */ - -task copyAclPlugin(dependsOn: ['jar']) { - doFirst { - new File(projectDir, '../eventmesh-security-plugin/dist/apps').mkdir() - new File(projectDir, '../dist/plugin/security').mkdirs() - } - doLast { - copy { - into('../eventmesh-security-plugin/dist/apps/') - from project.jar.getArchivePath() - exclude { - "eventmesh-security-plugin-${version}.jar" - "eventmesh-security-api-${version}.jar" - } - } - copy { - into '../dist/plugin/security' - from "../eventmesh-security-plugin/dist/apps/eventmesh-security-acl-${version}.jar" - } - } -} \ No newline at end of file + */ \ No newline at end of file diff --git a/eventmesh-security-plugin/eventmesh-security-api/src/main/java/org/apache/eventmesh/api/acl/AclService.java b/eventmesh-security-plugin/eventmesh-security-api/src/main/java/org/apache/eventmesh/api/acl/AclService.java index 3502e9076f..38b2f1a86f 100644 --- a/eventmesh-security-plugin/eventmesh-security-api/src/main/java/org/apache/eventmesh/api/acl/AclService.java +++ b/eventmesh-security-plugin/eventmesh-security-api/src/main/java/org/apache/eventmesh/api/acl/AclService.java @@ -17,11 +17,12 @@ package org.apache.eventmesh.api.acl; import org.apache.eventmesh.api.exception.AclException; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; import java.util.Properties; -@EventMeshSPI(isSingleton = true) +@EventMeshSPI(isSingleton = true, eventMeshExtensionType = EventMeshExtensionType.SECURITY) public interface AclService { void init() throws AclException; diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java index 6388809fcf..a096f1793d 100644 --- a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionFactory.java @@ -72,16 +72,16 @@ public static T getExtension(Class extensionType, String extensionName) { } @SuppressWarnings("unchecked") - private static T getSingletonExtension(Class extensionType, String extensionName) { - return (T) EXTENSION_INSTANCE_CACHE.computeIfAbsent(extensionName, name -> { - Class extensionInstanceClass = getExtensionClass(extensionType, extensionName); + private static T getSingletonExtension(Class extensionType, String extensionInstanceName) { + return (T) EXTENSION_INSTANCE_CACHE.computeIfAbsent(extensionInstanceName, name -> { + Class extensionInstanceClass = getExtensionInstanceClass(extensionType, extensionInstanceName); try { if (extensionInstanceClass == null) { return null; } T extensionInstance = extensionInstanceClass.newInstance(); - logger.info("initialize extension instance success, extensionType: {}, extensionName: {}", - extensionType, extensionName); + logger.info("initialize extension instance success, extensionType: {}, extensionInstanceName: {}", + extensionType, extensionInstanceName); return extensionInstance; } catch (InstantiationException | IllegalAccessException e) { throw new ExtensionException("Extension initialize error", e); @@ -89,15 +89,15 @@ private static T getSingletonExtension(Class extensionType, String extens }); } - private static T getPrototypeExtension(Class extensionType, String extensionName) { - Class extensionInstanceClass = getExtensionClass(extensionType, extensionName); + private static T getPrototypeExtension(Class extensionType, String extensionInstanceName) { + Class extensionInstanceClass = getExtensionInstanceClass(extensionType, extensionInstanceName); try { if (extensionInstanceClass == null) { return null; } T extensionInstance = extensionInstanceClass.newInstance(); logger.info("initialize extension instance success, extensionType: {}, extensionName: {}", - extensionType, extensionName); + extensionType, extensionInstanceName); return extensionInstance; } catch (InstantiationException | IllegalAccessException e) { throw new ExtensionException("Extension initialize error", e); @@ -105,10 +105,10 @@ private static T getPrototypeExtension(Class extensionType, String extens } @SuppressWarnings("unchecked") - private static Class getExtensionClass(Class extensionType, String extensionName) { + private static Class getExtensionInstanceClass(Class extensionType, String extensionInstanceName) { for (ExtensionClassLoader extensionClassLoader : extensionClassLoaders) { - Map> extensionClassMap = extensionClassLoader.loadExtensionClass(extensionType); - Class instanceClass = extensionClassMap.get(extensionName); + Map> extensionInstanceClassMap = extensionClassLoader.loadExtensionClass(extensionType, extensionInstanceName); + Class instanceClass = extensionInstanceClassMap.get(extensionInstanceName); if (instanceClass != null) { return (Class) instanceClass; } diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionType.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionType.java new file mode 100644 index 0000000000..786ae72443 --- /dev/null +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshExtensionType.java @@ -0,0 +1,40 @@ +/* + * 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; + +/** + * An Extension can be defined by extensionTypeName and extensionInstanceName + */ +public enum EventMeshExtensionType { + UNKNOWN("unknown"), + CONNECTOR("connector"), + REGISTRY("registry"), + SECURITY("security"), + ; + + private final String extensionTypeName; + + EventMeshExtensionType(String extensionTypeName) { + this.extensionTypeName = extensionTypeName; + } + + public String getExtensionTypeName() { + return extensionTypeName; + } + +} diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java index 48b0ef076b..364d197c9b 100644 --- a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/EventMeshSPI.java @@ -36,5 +36,11 @@ */ boolean isSingleton() default false; + /** + * {@link EventMeshExtensionType} + * @return extension type + */ + EventMeshExtensionType eventMeshExtensionType(); + } diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/EventMeshUrlClassLoader.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/EventMeshUrlClassLoader.java new file mode 100644 index 0000000000..c53543b6df --- /dev/null +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/EventMeshUrlClassLoader.java @@ -0,0 +1,56 @@ +/* + * 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.loader; + +import org.apache.commons.collections4.CollectionUtils; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +public class EventMeshUrlClassLoader extends URLClassLoader { + + public static EventMeshUrlClassLoader getInstance() { + return EventMeshUrlClassLoaderHolder.instance; + } + + /** + * Appends the specified URL to the list of URLs to search for classes and resources. + *

+ * If the URL specified is {@code null} or is already in the + * list of URLs, or if this loader is closed, then invoking this + * method has no effect. + *

+ * More detail see {@link URLClassLoader#addURL(URL)} + * @param urls + */ + public void addUrls(List urls) { + if (CollectionUtils.isEmpty(urls)) { + return; + } + urls.forEach(this::addURL); + } + + private EventMeshUrlClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + private static class EventMeshUrlClassLoaderHolder { + private static EventMeshUrlClassLoader instance = new EventMeshUrlClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); + } +} diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/ExtensionClassLoader.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/ExtensionClassLoader.java index feca359ee4..3e13c33c22 100644 --- a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/ExtensionClassLoader.java +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/ExtensionClassLoader.java @@ -29,11 +29,12 @@ public interface ExtensionClassLoader { /** - * load + * load extension class * - * @param extensionType extension type class - * @param extension type + * @param extensionType extension type class + * @param extensionInstanceName extension instance name + * @param extension type * @return extension instance name to extension instance class */ - Map> loadExtensionClass(Class extensionType); + Map> loadExtensionClass(Class extensionType, String extensionInstanceName); } diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/JarExtensionClassLoader.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/JarExtensionClassLoader.java index dde9fb95b6..3692bc1e0d 100644 --- a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/JarExtensionClassLoader.java +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/JarExtensionClassLoader.java @@ -17,8 +17,9 @@ package org.apache.eventmesh.spi.loader; +import com.google.common.base.Joiner; import com.google.common.collect.Lists; -import org.apache.commons.collections4.CollectionUtils; +import org.apache.eventmesh.spi.EventMeshSPI; import org.apache.eventmesh.spi.ExtensionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +29,7 @@ import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; @@ -47,25 +49,29 @@ public class JarExtensionClassLoader implements ExtensionClassLoader { new ConcurrentHashMap<>(16); private static final String EVENTMESH_EXTENSION_PLUGIN_DIR = System.getProperty("eventMeshPluginDir", - "./plugin"); + Joiner.on(File.separator).join(Lists.newArrayList(".", "plugin"))); - private static final String EVENTMESH_EXTENSION_META_DIR = "META-INF/eventmesh/"; + // META-INF/eventmesh + private static final String EVENTMESH_EXTENSION_META_DIR = Paths.get("META-INF", "eventmesh").toString(); @Override - public Map> loadExtensionClass(Class extensionType) { - return EXTENSION_CLASS_CACHE.computeIfAbsent(extensionType, this::doLoadExtensionClass); + public Map> loadExtensionClass(Class extensionType, String extensionInstanceName) { + return EXTENSION_CLASS_CACHE.computeIfAbsent(extensionType, t -> doLoadExtensionClass(t, extensionInstanceName)); } - private Map> doLoadExtensionClass(Class extensionType) { - Map> extensionMap = new HashMap<>(); + private Map> doLoadExtensionClass(Class extensionType, String extensionInstanceName) { + Map> extensionMap = new HashMap<>(16); + EventMeshSPI eventMeshSPIAnnotation = extensionType.getAnnotation(EventMeshSPI.class); - List pluginJarPaths = loadJarPathFromResource(EVENTMESH_EXTENSION_PLUGIN_DIR); - if (CollectionUtils.isEmpty(pluginJarPaths)) { - return extensionMap; - } + String pluginDir = Paths.get( + EVENTMESH_EXTENSION_PLUGIN_DIR, + eventMeshSPIAnnotation.eventMeshExtensionType().getExtensionTypeName(), + extensionInstanceName + ).toString(); - String extensionFileName = EVENTMESH_EXTENSION_META_DIR + extensionType.getName(); - URLClassLoader urlClassLoader = URLClassLoader.newInstance(pluginJarPaths.toArray(new URL[0])); + String extensionFileName = EVENTMESH_EXTENSION_META_DIR + File.separator + extensionType.getName(); + EventMeshUrlClassLoader urlClassLoader = EventMeshUrlClassLoader.getInstance(); + urlClassLoader.addUrls(loadJarPathFromResource(pluginDir)); try { Enumeration extensionUrls = urlClassLoader.getResources(extensionFileName); if (extensionUrls != null) { diff --git a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/MetaInfExtensionClassLoader.java b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/MetaInfExtensionClassLoader.java index 59d45e1396..ec07f4ea6f 100644 --- a/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/MetaInfExtensionClassLoader.java +++ b/eventmesh-spi/src/main/java/org/apache/eventmesh/spi/loader/MetaInfExtensionClassLoader.java @@ -43,14 +43,14 @@ public class MetaInfExtensionClassLoader implements ExtensionClassLoader { private static final String EVENTMESH_EXTENSION_META_DIR = "META-INF/eventmesh/"; @Override - public Map> loadExtensionClass(Class extensionType) { + public Map> loadExtensionClass(Class extensionType, String extensionInstanceName) { return EXTENSION_CLASS_CACHE.computeIfAbsent(extensionType, this::doLoadExtensionClass); } private Map> doLoadExtensionClass(Class extensionType) { Map> extensionMap = new HashMap<>(); String extensionFileName = EVENTMESH_EXTENSION_META_DIR + extensionType.getName(); - ClassLoader classLoader = MetaInfExtensionClassLoader.class.getClassLoader(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try { Enumeration extensionUrls = classLoader.getResources(extensionFileName); if (extensionUrls != null) { diff --git a/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestPrototypeExtension.java b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestPrototypeExtension.java index 0267c906a0..06e4549f54 100644 --- a/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestPrototypeExtension.java +++ b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestPrototypeExtension.java @@ -17,9 +17,10 @@ package org.apache.eventmesh.spi.example; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; -@EventMeshSPI(isSingleton = false) +@EventMeshSPI(isSingleton = false, eventMeshExtensionType = EventMeshExtensionType.UNKNOWN) public interface TestPrototypeExtension { void hello(); diff --git a/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestSingletonExtension.java b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestSingletonExtension.java index 11c61fe496..b78744d2e9 100644 --- a/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestSingletonExtension.java +++ b/eventmesh-spi/src/test/java/org/apache/eventmesh/spi/example/TestSingletonExtension.java @@ -17,9 +17,10 @@ package org.apache.eventmesh.spi.example; +import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; -@EventMeshSPI(isSingleton = true) +@EventMeshSPI(isSingleton = true, eventMeshExtensionType = EventMeshExtensionType.UNKNOWN) public interface TestSingletonExtension { void hello();