Skip to content

Commit

Permalink
refactor: add read-write lock to ExtensionContextRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
guqing committed Jul 17, 2023
1 parent 5a0e202 commit 96521a5
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package run.halo.app.plugin;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.lang.NonNull;

/**
* <p>Plugin application context registrar.</p>
Expand All @@ -18,7 +20,8 @@
public class ExtensionContextRegistry {
private static final ExtensionContextRegistry INSTANCE = new ExtensionContextRegistry();

private final Map<String, PluginApplicationContext> registry = new ConcurrentHashMap<>();
private final Map<String, PluginApplicationContext> registry = new HashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public static ExtensionContextRegistry getInstance() {
return INSTANCE;
Expand All @@ -27,35 +30,101 @@ public static ExtensionContextRegistry getInstance() {
private ExtensionContextRegistry() {
}

/**
* Acquire the read lock when using getPluginApplicationContexts and getByPluginId.
*/
public void acquireReadLock() {
this.readWriteLock.readLock().lock();
}

/**
* Release the read lock after using getPluginApplicationContexts and getByPluginId.
*/
public void releaseReadLock() {
this.readWriteLock.readLock().unlock();
}

/**
* Register plugin application context to registry map.
*
* @param pluginId plugin id(name)
* @param context plugin application context
*/
public void register(String pluginId, PluginApplicationContext context) {
registry.put(pluginId, context);
this.readWriteLock.writeLock().lock();
try {
registry.put(pluginId, context);
} finally {
this.readWriteLock.writeLock().unlock();
}
}

public PluginApplicationContext remove(String pluginId) {
return registry.remove(pluginId);
/**
* Remove plugin application context from registry map.
*
* @param pluginId plugin id
*/
public void remove(String pluginId) {
this.readWriteLock.writeLock().lock();
try {
PluginApplicationContext removed = registry.remove(pluginId);
if (removed != null) {
removed.close();
}
} finally {
this.readWriteLock.writeLock().unlock();
}
}

/**
* Gets plugin application context by plugin id from registry map.
* Note: ensure call {@link #containsContext(String)} after call this method.
*
* @param pluginId plugin id
* @return plugin application context
* @throws IllegalArgumentException if plugin id not found in registry
*/
@NonNull
public PluginApplicationContext getByPluginId(String pluginId) {
PluginApplicationContext context = registry.get(pluginId);
if (context == null) {
throw new IllegalArgumentException(
String.format("The plugin [%s] can not be found.", pluginId));
this.readWriteLock.readLock().lock();
try {
PluginApplicationContext context = registry.get(pluginId);
if (context == null) {
throw new IllegalArgumentException(
String.format("The plugin [%s] can not be found.", pluginId));
}
return context;
} finally {
this.readWriteLock.readLock().unlock();
}
return context;
}

/**
* Check whether the registry contains the plugin application context by plugin id.
*
* @param pluginId plugin id
* @return true if contains, otherwise false
*/
public boolean containsContext(String pluginId) {
return registry.containsKey(pluginId);
this.readWriteLock.readLock().lock();
try {
return registry.containsKey(pluginId);
} finally {
this.readWriteLock.readLock().unlock();
}
}

/**
* Gets all plugin application contexts from registry map.
*
* @return plugin application contexts
*/
public List<PluginApplicationContext> getPluginApplicationContexts() {
return new ArrayList<>(registry.values());
this.readWriteLock.readLock().lock();
try {
return new ArrayList<>(registry.values());
} finally {
this.readWriteLock.readLock().unlock();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ public void onApplicationEvent(ApplicationEvent event) {
if (!isSharedEventAnnotationPresent(event.getClass())) {
return;
}
List<PluginApplicationContext> pluginApplicationContexts =
ExtensionContextRegistry.getInstance().getPluginApplicationContexts();
for (PluginApplicationContext pluginApplicationContext : pluginApplicationContexts) {
log.debug("Bridging broadcast event [{}] to plugin [{}]", event,
pluginApplicationContext.getPluginId());
pluginApplicationContext.publishEvent(event);
ExtensionContextRegistry.getInstance().acquireReadLock();
try {
List<PluginApplicationContext> pluginApplicationContexts =
ExtensionContextRegistry.getInstance().getPluginApplicationContexts();
for (PluginApplicationContext pluginApplicationContext : pluginApplicationContexts) {
log.debug("Bridging broadcast event [{}] to plugin [{}]", event,
pluginApplicationContext.getPluginId());
pluginApplicationContext.publishEvent(event);
}
} finally {
ExtensionContextRegistry.getInstance().releaseReadLock();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,7 @@ public PluginApplicationContext getPluginApplicationContext(String pluginId) {

public void contextDestroyed(String pluginId) {
Assert.notNull(pluginId, "pluginId must not be null");
PluginApplicationContext removed = contextRegistry.remove(pluginId);
if (removed != null) {
removed.close();
}
contextRegistry.remove(pluginId);
}

private Set<Class<?>> findCandidateComponents(String pluginId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.ArrayList;
import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerFunction;
Expand Down Expand Up @@ -48,27 +49,35 @@ public void accept(@NonNull RouterFunctions.Visitor visitor) {

@SuppressWarnings("unchecked")
private List<RouterFunction<ServerResponse>> routerFunctions() {
var rawRouterFunctions = getInstance().getPluginApplicationContexts()
.stream()
.flatMap(applicationContext -> applicationContext
.getBeanProvider(RouterFunction.class)
.orderedStream())
.map(router -> (RouterFunction<ServerResponse>) router)
.toList();
var reverseProxies = reverseProxyRouterFunctionFactory.getRouterFunctions();
getInstance().acquireReadLock();
try {
List<PluginApplicationContext> contexts = getInstance().getPluginApplicationContexts()
.stream()
.filter(AbstractApplicationContext::isActive)
.toList();
var rawRouterFunctions = contexts
.stream()
.flatMap(applicationContext -> applicationContext
.getBeanProvider(RouterFunction.class)
.orderedStream())
.map(router -> (RouterFunction<ServerResponse>) router)
.toList();
var reverseProxies = reverseProxyRouterFunctionFactory.getRouterFunctions();

var endpointBuilder = new CustomEndpointsBuilder();
getInstance().getPluginApplicationContexts()
.forEach(context -> context.getBeanProvider(CustomEndpoint.class)
var endpointBuilder = new CustomEndpointsBuilder();
contexts.forEach(context -> context.getBeanProvider(CustomEndpoint.class)
.orderedStream()
.forEach(endpointBuilder::add));
var customEndpoint = endpointBuilder.build();
var customEndpoint = endpointBuilder.build();

List<RouterFunction<ServerResponse>> routerFunctions =
new ArrayList<>(rawRouterFunctions.size() + reverseProxies.size() + 1);
routerFunctions.addAll(rawRouterFunctions);
routerFunctions.addAll(reverseProxies);
routerFunctions.add(customEndpoint);
return routerFunctions;
List<RouterFunction<ServerResponse>> routerFunctions =
new ArrayList<>(rawRouterFunctions.size() + reverseProxies.size() + 1);
routerFunctions.addAll(rawRouterFunctions);
routerFunctions.addAll(reverseProxies);
routerFunctions.add(customEndpoint);
return routerFunctions;
} finally {
getInstance().releaseReadLock();
}
}
}

0 comments on commit 96521a5

Please sign in to comment.