From 4098297e51832b00ac839fe28e199f296a48868b Mon Sep 17 00:00:00 2001 From: Daydreamer-ia <2296032269@qq.com> Date: Sun, 28 Aug 2022 17:38:08 +0800 Subject: [PATCH] Add the base implements for ability control. --- .../AbstractAbilityControlManager.java | 401 ++++++++++++++++++ .../ability/DefaultAbilityControlManager.java | 343 +++++++++++++++ .../ability/discover/AbilityHandleLoader.java | 49 +++ .../discover/NacosAbilityManagerHolder.java | 92 ++++ 4 files changed, 885 insertions(+) create mode 100644 common/src/main/java/com/alibaba/nacos/common/ability/AbstractAbilityControlManager.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/ability/DefaultAbilityControlManager.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/ability/discover/AbilityHandleLoader.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/ability/discover/NacosAbilityManagerHolder.java diff --git a/common/src/main/java/com/alibaba/nacos/common/ability/AbstractAbilityControlManager.java b/common/src/main/java/com/alibaba/nacos/common/ability/AbstractAbilityControlManager.java new file mode 100644 index 000000000..f78fd0a20 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/ability/AbstractAbilityControlManager.java @@ -0,0 +1,401 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common.ability; + +import com.alibaba.nacos.api.ability.constant.AbilityKey; +import com.alibaba.nacos.api.ability.constant.AbilityStatus; +import com.alibaba.nacos.api.ability.entity.AbilityTable; +import com.alibaba.nacos.common.ability.handler.AbilityHandlePreProcessor; +import com.alibaba.nacos.common.ability.inter.TraceableAbilityControlManager; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.atomic.AtomicReference; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.locks.ReentrantLock; + +/**. + * @author Daydreamer + * @description Base class for ability control. It can only be used internally by Nacos.It showld be sington. + * @date 2022/7/12 19:18 + **/ +@SuppressWarnings("all") +public abstract class AbstractAbilityControlManager implements TraceableAbilityControlManager { + + + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAbilityControlManager.class); + + /** + * Abilities current supporting + *

+ * key: ability key from {@link AbilityKey} + * value: whether to turn on + */ + protected final Map currentRunningAbility = new ConcurrentHashMap<>(); + + /** + * Ability table collections + *

+ * key: connectionId + * value: AbilityTable + */ + protected final Map nodeAbilityTable = new ConcurrentHashMap<>(); + + /**. + * These handlers will be invoke before combine the ability table + */ + private final List abilityHandlePreProcessors = new ArrayList<>(); + + /** + * This map is used to trace the status of ability table. + * Its status should be update after {@link #addNewTable(AbilityTable)} and {@link #removeTable(String)} + */ + protected final Map> abilityStatus = new ConcurrentHashMap<>(); + + private final ReentrantLock lockForProcessors = new ReentrantLock(); + + private final ReentrantLock lockForAbilityTable = new ReentrantLock(); + + protected AbstractAbilityControlManager() { + // register events + registerAbilityEvent(); + // put abilities + currentRunningAbility.putAll(AbilityKey.getCurrentNodeSupportAbility()); + // initialize + init(); + } + + + private void registerAbilityEvent(){ + // register events + NotifyCenter.registerToPublisher(AbilityComeEvent.class, 16384); + NotifyCenter.registerToPublisher(AbilityExpiredEvent.class, 16384); + } + + /** + * Whether the ability current node supporting is running. Return false if current node doesn't support. + * + * @param abilityKey ability key + * @return is running + */ + @Override + public boolean isCurrentNodeAbilityRunning(String abilityKey) { + return currentRunningAbility.getOrDefault(abilityKey, false); + } + + /** + * Register a new ability table. + * + * @param table the ability table. + */ + @Override + public final void addNewTable(AbilityTable table) { + // id should not be null + String connectionId = table.getConnectionId(); + // if exists + if (contains(connectionId) || connectionId == null) { + return; + } + lockForAbilityTable.lock(); + try { + // check + if (contains(connectionId)) { + return; + } + // update status + abilityStatus.put(connectionId, new AtomicReference<>(AbilityStatus.INITIALIZING)); + // handle ability table before joining current node + AbilityTable processed = process(table); + // hook method + add(processed); + // add to node + nodeAbilityTable.put(connectionId, table); + } finally { + lockForAbilityTable.unlock(); + } + // update status + AtomicReference abilityStatusAtomicReference = abilityStatus.get(table.getConnectionId()); + if (abilityStatusAtomicReference != null) { + // try one time + // do nothing if AbilityStatus == Expired + // if ready + if(abilityStatusAtomicReference.compareAndSet(AbilityStatus.INITIALIZING, AbilityStatus.READY)) { + // publish event to subscriber + AbilityComeEvent updateEvent = new AbilityComeEvent(); + updateEvent.setConnectionId(table.getConnectionId()); + updateEvent.setTable(table); + NotifyCenter.publishEvent(updateEvent); + } + } else { + LOGGER.warn("[AbiityControlManager] Cannot get connection status after processing ability table, possible reason is that the network is unstable"); + } + } + + /** + * Remove a ability table + * + * @param connectionId the ability table which is removing. + */ + @Override + public final void removeTable(String connectionId) { + // if not exists + if(connectionId == null || !nodeAbilityTable.containsKey(connectionId)){ + return; + } + AbilityTable removingTable = null; + lockForAbilityTable.lock(); + try { + // check + if (!nodeAbilityTable.containsKey(connectionId)) { + return; + } + nodeAbilityTable.get(connectionId); + // update status + abilityStatus.computeIfPresent(connectionId, (k, v) -> { + v.set(AbilityStatus.EXPIRED); + return v; + }); + // hook method + remove(connectionId); + // remove + nodeAbilityTable.remove(connectionId); + } finally { + lockForAbilityTable.unlock(); + } + // remove status + abilityStatus.remove(connectionId); + // publish event + if (removingTable != null) { + AbilityExpiredEvent expiredEvent = new AbilityExpiredEvent(); + expiredEvent.setTable(removingTable); + expiredEvent.setConnectionId(connectionId); + NotifyCenter.publishEvent(expiredEvent); + } + } + + + /** + * Register a new ability table. This is a ThreadSafe method for {@link AbstractAbilityControlManager#remove(String)}. + * + * @param table the ability table. + */ + protected abstract void add(AbilityTable table); + + + /** + * Remove a ability table. This is a ThreadSafe method for {@link AbstractAbilityControlManager#add(AbilityTable)}. + * + * @param connectionId the ability table which is removing. + */ + protected abstract void remove(String connectionId); + + + /** + * wthether contains this ability table + * + * @return + */ + @Override + public boolean contains(String connectionId) { + return nodeAbilityTable.containsKey(connectionId); + } + + + /** + * Get the status of the ability table. + * + * @param connectionId connection id + * @return status of ability table {@link AbilityStatus} + */ + @Override + public AbilityStatus trace(String connectionId) { + if (connectionId == null) { + return AbilityStatus.NOT_EXIST; + } + return abilityStatus.getOrDefault(connectionId, new AtomicReference<>(AbilityStatus.NOT_EXIST)).get(); + } + + /** + * Trace the status of connection if

{@link AbilityStatus#INITIALIZING}

, wake up if

{@link AbilityStatus#READY}

+ * It will return if status is

{@link AbilityStatus#EXPIRED}

or

{@link AbilityStatus#NOT_EXIST}

+ * + * @param connectionId connection id + * @param source source status + * @param target target status + * @return if success + */ + @Override + public boolean traceReadySyn(String connectionId) { + AbilityStatus source = AbilityStatus.INITIALIZING; + AbilityStatus target = AbilityStatus.READY; + AtomicReference atomicReference = abilityStatus.get(connectionId); + // return if null + if (atomicReference == null || atomicReference.get().equals(AbilityStatus.EXPIRED)) { + return false; + } else if (target == atomicReference.get()) { + return true; + } + // try if status legal + while (!atomicReference.get().equals(target) && atomicReference.get().equals(source)) { + LockSupport.parkNanos(100L); + // if expired + if (atomicReference.get().equals(AbilityStatus.EXPIRED)) { + return false; + } + } + return atomicReference.get().equals(target); + } + + /**. + * Invoking {@link AbilityHandlePreProcessor} + * + * @param source source ability table + * @return result + */ + protected AbilityTable process(AbilityTable source) { + // do nothing if no processor + if (CollectionUtils.isEmpty(abilityHandlePreProcessors)) { + return source; + } + // copy to advoid error process + AbilityTable abilityTable = source; + AbilityTable copy = new AbilityTable(source.getConnectionId(), new HashMap<>(source.getAbility()), source.isServer(), source.getVersion()); + for (AbilityHandlePreProcessor handler : abilityHandlePreProcessors) { + try { + abilityTable = handler.handle(abilityTable); + } catch (Throwable t) { + LOGGER.warn("[AbilityHandlePostProcessor] Failed to invoke {} :{}", + handler.getClass().getSimpleName(), t.getLocalizedMessage()); + // ensure normal operation + abilityTable = copy; + } + } + return abilityTable; + } + + /**. + * They will be invoked before updating ability table, but the order in which + * they are called cannot be guaranteed + * + * @param postProcessor PostProcessor instance + */ + @Override + public void addPostProcessor(AbilityHandlePreProcessor postProcessor) { + lockForProcessors.lock(); + try { + abilityHandlePreProcessors.add(postProcessor); + } finally { + lockForProcessors.unlock(); + LOGGER.info("[AbilityHandlePostProcessor] registry handler: {}", + postProcessor.getClass().getSimpleName()); + } + } + + /** + * Initialize the manager + */ + @Override + public void init() { + // default init + // nothing to do + } + + /** + * It should be invoked before destroy + */ + @Override + public void destroy() { + // default destroy + // nothing to do + } + + /** + * Return ability table of current node + * + * @return ability table + */ + @Override + public Map getCurrentRunningAbility() { + return new HashMap<>(this.currentRunningAbility); + } + + /** + * base class for ability + */ + public abstract class AbilityEvent extends Event { + + private static final long serialVersionUID = -123241121302761L; + + protected AbilityEvent(){} + + /** + * connection id. + */ + private String connectionId; + + /** + * ability table + */ + private AbilityTable table; + + + public String getConnectionId() { + return connectionId; + } + + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + + public AbilityTable getTable() { + return table; + } + + public void setTable(AbilityTable table) { + this.table = table; + } + } + + /** + * when a connection connected. + */ + public class AbilityComeEvent extends AbilityEvent { + + private static final long serialVersionUID = -123241121302761L; + + private AbilityComeEvent(){} + } + + /** + * when a connection disconnected. + */ + public class AbilityExpiredEvent extends AbilityEvent { + + private static final long serialVersionUID = -123241121212127619L; + + private AbilityExpiredEvent(){} + + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/ability/DefaultAbilityControlManager.java b/common/src/main/java/com/alibaba/nacos/common/ability/DefaultAbilityControlManager.java new file mode 100644 index 000000000..b3012392e --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/ability/DefaultAbilityControlManager.java @@ -0,0 +1,343 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common.ability; + +import com.alibaba.nacos.api.ability.constant.AbilityKey; +import com.alibaba.nacos.api.ability.entity.AbilityTable; +import com.alibaba.nacos.common.JustForTest; +import com.alibaba.nacos.common.ability.handler.HandlerMapping; +import com.alibaba.nacos.common.ability.inter.AbilityHandlerRegistry; +import com.alibaba.nacos.common.executor.ExecutorFactory; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.MapUtil; +import com.alibaba.nacos.common.utils.ThreadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/**. + * @author Daydreamer + * @description It is a relatively complete capability control center implementation. + * @date 2022/7/12 19:18 + **/ +public abstract class DefaultAbilityControlManager extends AbstractAbilityControlManager + implements AbilityHandlerRegistry { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAbilityControlManager.class); + + /** + * . These handlers will be invoked when the flag of ability change key: ability key from {@link AbilityKey} value: + * componments who want to be invoked if its interested ability turn on/off + */ + private final Map> handlerMappings = new ConcurrentHashMap<>(); + + /**. + * run for HandlerMapping + */ + private final Executor simpleThreadPool = ExecutorFactory.newSingleExecutorService(); + + private final ReentrantLock lockForHandlerMappings = new ReentrantLock(); + + protected DefaultAbilityControlManager() { + ThreadUtils.addShutdownHook(this::destroy); + NotifyCenter.registerToPublisher(AbilityUpdateEvent.class, 16384); + } + + /** + * . Turn on the ability whose key is

abilityKey

+ * + * @param abilityKey ability key + * @return if turn success + */ + @Override + public boolean enableCurrentNodeAbility(String abilityKey) { + return doTurn(true, abilityKey); + } + + /** + * . Turn off the ability whose key is

abilityKey

+ * + * @param abilityKey ability key + * @return if turn success + */ + @Override + public boolean disableCurrentNodeAbility(String abilityKey) { + return doTurn(false, abilityKey); + } + + /** + * . Turn on/off the ability of current node + * + * @param isOn is on + * @param abilityKey ability key from {@link AbilityKey} + * @return if turn success + */ + private boolean doTurn(boolean isOn, String abilityKey) { + Boolean isEnabled = currentRunningAbility.get(abilityKey); + // if not supporting this key + if (isEnabled == null) { + LOGGER.warn("[AbilityControlManager] Attempt to turn on/off a not existed ability!"); + return false; + } else if (isOn == isEnabled) { + // if already turn on/off + return true; + } + // turn on/off + currentRunningAbility.put(abilityKey, isOn); + // handler mappings + triggerHandlerMappingAsyn(abilityKey, isOn, this.handlerMappings); + // notify event + AbilityUpdateEvent abilityUpdateEvent = new AbilityUpdateEvent(); + abilityUpdateEvent.setTable(new AbilityTable().setAbility(Collections.unmodifiableMap(currentRunningAbility))); + abilityUpdateEvent.isOn = isOn; + abilityUpdateEvent.abilityKey = abilityKey; + NotifyCenter.publishEvent(abilityUpdateEvent); + return true; + } + + /** + * Register the component which is managed by {@link AbstractAbilityControlManager}. if you are hoping that a + * component will be invoked when turn on/off the ability whose key is

abilityKey

. + * + * @param abilityKey component key. + * @param priority the higher the priority is, the faster it will be called. + * @param handlerMapping component instance. + */ + @Override + public void registerComponent(String abilityKey, HandlerMapping handlerMapping, int priority) { + doRegisterComponent(abilityKey, handlerMapping, this.handlerMappings, lockForHandlerMappings, priority, currentRunningAbility); + } + + @Override + public int removeComponent(String abilityKey, Class handlerMappingClazz) { + return doRemove(abilityKey, handlerMappingClazz, lockForHandlerMappings, handlerMappings); + } + + @Override + public final void destroy() { + LOGGER.warn("[DefaultAbilityControlManager] - Start destroying..."); + ((ThreadPoolExecutor) simpleThreadPool).shutdown(); + if (MapUtil.isNotEmpty(handlerMappings)) { + handlerMappings.keySet().forEach(key -> doTriggerSyn(key, false, handlerMappings)); + } + // hook + doDestroy(); + LOGGER.warn("[DefaultAbilityControlManager] - Destruction of the end"); + } + + /**. + * hook for subclass + */ + protected void doDestroy() { + // for server ability manager + } + + /** + * Remove the component instance of

handlerMappingClazz

. + * + * @param abilityKey ability key from {@link com.alibaba.nacos.api.ability.constant.AbilityKey} + * @param handlerMappingClazz implement of {@link HandlerMapping} + * @param lock lock for operation + * @param handlerMappingsMap handler collection map + * @return the count of components have removed + */ + protected int doRemove(String abilityKey, Class handlerMappingClazz, Lock lock, + Map> handlerMappingsMap) { + List handlerMappings = handlerMappingsMap.get(abilityKey); + if (CollectionUtils.isEmpty(handlerMappings)) { + return 0; + } + lock.lock(); + try { + AtomicInteger count = new AtomicInteger(); + handlerMappings.removeIf(item -> { + if (item.handlerMapping.getClass().equals(handlerMappingClazz)) { + count.getAndIncrement(); + return true; + } + return false; + }); + return count.get(); + } finally { + lock.unlock(); + } + } + + @Override + public int removeAll(String abilityKey) { + List remove = this.handlerMappings.remove(abilityKey); + return Optional.ofNullable(remove).orElse(Collections.emptyList()).size(); + } + + /**. + * Register the component into handlerMappings locking by lockForHandlerMappings to ensure concurrency security. + * + * @param abilityKey ability key + * @param handlerMapping component instance. + * @param handlerMappings container + * @param lockForHandlerMappings lock to ensure concurrency + * @param abilityTable behavioral basis of handler + */ + protected void doRegisterComponent(String abilityKey, HandlerMapping handlerMapping, + Map> handlerMappings, Lock lockForHandlerMappings, + int priority, Map abilityTable) { + if (!currentRunningAbility.containsKey(abilityKey)) { + LOGGER.warn("[AbilityHandlePostProcessor] Failed to register processor: {}, because illegal key!", + handlerMapping.getClass().getSimpleName()); + } + + // legal key + lockForHandlerMappings.lock(); + try { + List handlers = handlerMappings.getOrDefault(abilityKey, new CopyOnWriteArrayList<>()); + HandlerWithPriority handlerWithPriority = new HandlerWithPriority(handlerMapping, priority); + handlers.add(handlerWithPriority); + handlerMappings.put(abilityKey, handlers); + // choose behavior + // enable default + if (abilityTable.getOrDefault(abilityKey, false)) { + handlerMapping.enable(); + } else { + handlerMapping.disable(); + } + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error("[DefaultAbilityControlManager] Fail to register handler: {}", handlerMapping.getClass().getSimpleName()); + } finally { + lockForHandlerMappings.unlock(); + LOGGER.info("[DefaultAbilityControlManager] Successfully registered processor: {}", + handlerMapping.getClass().getSimpleName()); + } + } + + /** + * Invoke componments which linked to ability key asyn. + * + * @param key ability key from {@link AbilityKey} + * @param isEnabled turn on/off + * @param handlerMappingsMap handler collection + */ + protected void triggerHandlerMappingAsyn(String key, boolean isEnabled, + Map> handlerMappingsMap) { + simpleThreadPool.execute(() -> doTriggerSyn(key, isEnabled, handlerMappingsMap)); + } + + /** + * Invoke componments which linked to ability key syn. + * + * @param key ability key from {@link AbilityKey} + * @param isEnabled turn on/off + * @param handlerMappingsMap handler collection + */ + protected void doTriggerSyn(String key, boolean isEnabled, + Map> handlerMappingsMap) { + List handlerWithPriorities = handlerMappingsMap.get(key); + // return if empty + if (CollectionUtils.isEmpty(handlerWithPriorities)) { + return; + } + Collections.sort(handlerWithPriorities); + // invoked all + handlerWithPriorities.forEach(handlerMappingWithPriorities -> { + // any error from current handler does not affect other handler + HandlerMapping handlerMapping = handlerMappingWithPriorities.handlerMapping; + try { + if (isEnabled) { + handlerMapping.enable(); + } else { + handlerMapping.disable(); + } + } catch (Throwable t) { + LOGGER.warn("[HandlerMapping] Failed to invoke {} :{}", handlerMapping.getClass().getSimpleName(), + t.getLocalizedMessage()); + } + }); + } + + @JustForTest + protected Map> handlerMapping() { + return this.handlerMappings; + } + + /** + * Support priority handler. + */ + protected class HandlerWithPriority implements Comparable { + + /**. + * Decorated + */ + public HandlerMapping handlerMapping; + + /**. + * the higher the priority, the faster it will be called + */ + public int priority; + + public HandlerWithPriority(HandlerMapping handlerMapping, int priority) { + this.handlerMapping = handlerMapping; + this.priority = priority; + } + + @Override + public int compareTo(HandlerWithPriority o) { + return o.priority - this.priority; + } + } + + /**. + * notify when current node ability changing + */ + public class AbilityUpdateEvent extends AbilityEvent { + + private static final long serialVersionUID = -1232411212311111L; + + private String abilityKey; + + private boolean isOn; + + private AbilityUpdateEvent(){} + + public String getAbilityKey() { + return abilityKey; + } + + public void setAbilityKey(String abilityKey) { + this.abilityKey = abilityKey; + } + + public boolean isOn() { + return isOn; + } + + public void setOn(boolean on) { + isOn = on; + } + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/ability/discover/AbilityHandleLoader.java b/common/src/main/java/com/alibaba/nacos/common/ability/discover/AbilityHandleLoader.java new file mode 100644 index 000000000..feb07436a --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/ability/discover/AbilityHandleLoader.java @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common.ability.discover; + +import com.alibaba.nacos.common.ability.handler.AbilityHandlePreProcessor; +import com.alibaba.nacos.common.spi.NacosServiceLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; + +/**. + * @author Daydreamer + * @description It is spi loader to load {@link AbilityHandlePreProcessor} + * @date 2022/8/25 18:24 + **/ +public class AbilityHandleLoader { + + private final Collection initializers; + + private static final Logger LOGGER = LoggerFactory.getLogger(AbilityHandleLoader.class); + + public AbilityHandleLoader() { + initializers = new HashSet<>(); + for (AbilityHandlePreProcessor preProcessor : NacosServiceLoader.load(AbilityHandlePreProcessor.class)) { + initializers.add(preProcessor); + LOGGER.info("Load {} for AbilityHandlePreProcessor", preProcessor.getClass().getCanonicalName()); + } + } + + public Collection getInitializers() { + return initializers; + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/ability/discover/NacosAbilityManagerHolder.java b/common/src/main/java/com/alibaba/nacos/common/ability/discover/NacosAbilityManagerHolder.java new file mode 100644 index 000000000..fe7a0c760 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/ability/discover/NacosAbilityManagerHolder.java @@ -0,0 +1,92 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common.ability.discover; + +import com.alibaba.nacos.common.ability.AbstractAbilityControlManager; +import com.alibaba.nacos.common.ability.DefaultAbilityControlManager; +import com.alibaba.nacos.common.ability.inter.AbilityControlManager; +import com.alibaba.nacos.common.spi.NacosServiceLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.ServiceConfigurationError; + +/** + * This class is used to discover {@link AbstractAbilityControlManager} implements. All the + * ability operation will be finish in this singleton. + * + * @author Daydreamer + * @date 2022/7/14 19:58 + **/ +public class NacosAbilityManagerHolder { + + /**. + * private constructor + */ + private NacosAbilityManagerHolder() { + } + + private static final Logger LOGGER = LoggerFactory.getLogger(NacosAbilityManagerHolder.class); + + /**. + * singleton + */ + private static DefaultAbilityControlManager abstractAbilityControlManager; + + static { + // spi discover implement + Collection load = null; + try { + // if server + load = NacosServiceLoader.load(DefaultAbilityControlManager.class); + } catch (ServiceConfigurationError e) { + // if client or not ability control manager + load = NacosServiceLoader.load(DefaultAbilityControlManager.class); + } + // the priority of the server is higher + if (load.size() > 0) { + load.forEach(clazz -> { + abstractAbilityControlManager = clazz; + }); + LOGGER.info("[AbilityControlManager] Successfully initialize AbilityControlManager"); + // init pre processor + AbilityHandleLoader loader = new AbilityHandleLoader(); + loader.getInitializers().forEach(processor -> abstractAbilityControlManager.addPostProcessor(processor)); + } + } + + /**. + * get nacos ability control manager + * + * @return BaseAbilityControlManager + */ + public static DefaultAbilityControlManager getInstance() { + return abstractAbilityControlManager; + } + + /**. + * Return the target type of ability manager + * + * @param clazz clazz + * @param target type + * @return AbilityControlManager + */ + public static T getInstance(Class clazz) { + return clazz.cast(abstractAbilityControlManager); + } +}