Skip to content

Commit c18adf7

Browse files
authored
[ISSUE #367]Enhance SPI plugins (#419)
1 parent 3e22c6a commit c18adf7

File tree

12 files changed

+356
-2
lines changed

12 files changed

+356
-2
lines changed

eventmesh-common/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
#
1717
group=org.apache.eventmesh
1818
version=1.2.0-SNAPSHOT
19-
jdk=1.7
19+
jdk=1.8

eventmesh-spi/build.gradle

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/

eventmesh-spi/gradle.properties

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
group=org.apache.eventmesh
18+
version=1.2.0-SNAPSHOT
19+
jdk=1.8
20+
snapshot=false
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
22+
public enum EventMeshExtensionFactory {
23+
;
24+
25+
public static <T> T getExtension(Class<T> extensionType, String extensionName) {
26+
if (extensionType == null) {
27+
throw new ExtensionException("extensionType is null");
28+
}
29+
if (StringUtils.isEmpty(extensionName)) {
30+
throw new ExtensionException("extensionName is null");
31+
}
32+
if (!extensionType.isInterface() || !extensionType.isAnnotationPresent(EventMeshSPI.class)) {
33+
throw new ExtensionException(String.format("extensionType:%s is invalided", extensionType));
34+
}
35+
return EventMeshExtensionLoader.getExtension(extensionType, extensionName);
36+
}
37+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.net.URL;
23+
import java.util.Enumeration;
24+
import java.util.Properties;
25+
import java.util.concurrent.ConcurrentHashMap;
26+
27+
public enum EventMeshExtensionLoader {
28+
;
29+
30+
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Class<?>>> EXTENSION_CLASS_LOAD_CACHE = new ConcurrentHashMap<>(16);
31+
32+
private static final ConcurrentHashMap<String, Object> EXTENSION_INSTANCE_CACHE = new ConcurrentHashMap<>(16);
33+
34+
private static final String EVENTMESH_EXTENSION_DIR = "META-INF/eventmesh/";
35+
36+
@SuppressWarnings("unchecked")
37+
public static <T> T getExtension(Class<T> extensionType, String extensionName) {
38+
if (!hasLoadExtensionClass(extensionType)) {
39+
loadExtensionClass(extensionType);
40+
}
41+
if (!hasInitializeExtension(extensionName)) {
42+
initializeExtension(extensionType, extensionName);
43+
}
44+
return (T) EXTENSION_INSTANCE_CACHE.get(extensionName);
45+
}
46+
47+
private static <T> void initializeExtension(Class<T> extensionType, String extensionName) {
48+
ConcurrentHashMap<String, Class<?>> extensionClassMap = EXTENSION_CLASS_LOAD_CACHE.get(extensionType);
49+
if (extensionClassMap == null) {
50+
throw new ExtensionException(String.format("Extension type:%s has not been loaded", extensionType));
51+
}
52+
if (!extensionClassMap.containsKey(extensionName)) {
53+
throw new ExtensionException(String.format("Extension name:%s has not been loaded", extensionName));
54+
}
55+
Class<?> aClass = extensionClassMap.get(extensionName);
56+
try {
57+
EXTENSION_INSTANCE_CACHE.put(extensionName, aClass.newInstance());
58+
} catch (InstantiationException | IllegalAccessException e) {
59+
throw new ExtensionException("Extension initialize error", e);
60+
}
61+
}
62+
63+
public static <T> void loadExtensionClass(Class<T> extensionType) {
64+
String extensionFileName = EVENTMESH_EXTENSION_DIR + extensionType.getName();
65+
ClassLoader classLoader = EventMeshExtensionLoader.class.getClassLoader();
66+
try {
67+
Enumeration<URL> extensionUrls = classLoader.getResources(extensionFileName);
68+
if (extensionUrls != null) {
69+
while (extensionUrls.hasMoreElements()) {
70+
URL url = extensionUrls.nextElement();
71+
loadResources(url, extensionType);
72+
}
73+
}
74+
} catch (IOException e) {
75+
throw new ExtensionException("load extension class error", e);
76+
}
77+
78+
79+
}
80+
81+
private static <T> void loadResources(URL url, Class<T> extensionType) throws IOException {
82+
try (InputStream inputStream = url.openStream()) {
83+
Properties properties = new Properties();
84+
properties.load(inputStream);
85+
properties.forEach((extensionName, extensionClass) -> {
86+
String extensionNameStr = (String) extensionName;
87+
String extensionClassStr = (String) extensionClass;
88+
try {
89+
Class<?> targetClass = Class.forName(extensionClassStr);
90+
if (!extensionType.isAssignableFrom(targetClass)) {
91+
throw new ExtensionException(
92+
String.format("class: %s is not subClass of %s", targetClass, extensionType));
93+
}
94+
EXTENSION_CLASS_LOAD_CACHE.computeIfAbsent(extensionType, k -> new ConcurrentHashMap<>())
95+
.put(extensionNameStr, targetClass);
96+
} catch (ClassNotFoundException e) {
97+
throw new ExtensionException("load extension class error", e);
98+
}
99+
});
100+
}
101+
}
102+
103+
private static <T> boolean hasLoadExtensionClass(Class<T> extensionType) {
104+
return EXTENSION_CLASS_LOAD_CACHE.containsKey(extensionType);
105+
}
106+
107+
private static boolean hasInitializeExtension(String extensionName) {
108+
return EXTENSION_INSTANCE_CACHE.containsKey(extensionName);
109+
}
110+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
import java.lang.annotation.Documented;
21+
import java.lang.annotation.ElementType;
22+
import java.lang.annotation.Retention;
23+
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
25+
26+
/**
27+
* Just as a marker for SPI
28+
*/
29+
@Documented
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@Target({ElementType.TYPE})
32+
public @interface EventMeshSPI {
33+
34+
}
35+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
public class ExtensionException extends RuntimeException {
21+
22+
public ExtensionException(Exception e) {
23+
super(e);
24+
}
25+
26+
public ExtensionException(String message) {
27+
super(message);
28+
}
29+
30+
public ExtensionException(String message, Exception e) {
31+
super(message, e);
32+
}
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
import org.junit.Test;
21+
22+
public class EventMeshExtensionFactoryTest {
23+
24+
@Test
25+
public void getExtension() {
26+
TestExtension extensionA = EventMeshExtensionFactory.getExtension(TestExtension.class, "extensionA");
27+
extensionA.hello();
28+
}
29+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
public class ExtensionA implements TestExtension {
21+
22+
@Override
23+
public void hello() {
24+
System.out.println("I am ExtensionA");
25+
}
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.eventmesh.spi;
19+
20+
@EventMeshSPI
21+
public interface TestExtension {
22+
23+
void hello();
24+
}

0 commit comments

Comments
 (0)