XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
package com.xfestudio.xfeservermanager.core.trigger;

import com.xfestudio.xfeservermanager.api.trigger.TriggerExtension;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;

/** Discovers declarative trigger extensions while isolating invalid providers. */
public final class TriggerExtensionRegistry {
    private TriggerExtensionRegistry() { }

    public static Snapshot discover(Collection<TriggerExtension.Descriptor> builtIns, ClassLoader classLoader) {
        Objects.requireNonNull(builtIns, "builtIns");
        Map<String, TriggerExtension.Descriptor> descriptors = new LinkedHashMap<>();
        builtIns.forEach(descriptor -> descriptors.put(key(descriptor), descriptor));
        Map<String, TriggerExtension.FunctionHandler> functions = new LinkedHashMap<>();
        Map<String, TriggerExtension.ActionHandler> actions = new LinkedHashMap<>();
        List<String> diagnostics = new ArrayList<>();
        List<ServiceLoader.Provider<TriggerExtension>> providers;
        try {
            providers = ServiceLoader.load(TriggerExtension.class,
                    classLoader == null ? TriggerExtensionRegistry.class.getClassLoader() : classLoader)
                    .stream().toList();
        } catch (RuntimeException | java.util.ServiceConfigurationError failure) {
            diagnostics.add("trigger extension discovery failed: " + safeMessage(failure));
            return snapshot(descriptors, functions, actions, diagnostics);
        }
        for (ServiceLoader.Provider<TriggerExtension> provider : providers) {
            try {
                Map<String, TriggerExtension.Descriptor> candidateDescriptors =
                        new LinkedHashMap<>(descriptors);
                Map<String, TriggerExtension.FunctionHandler> candidateFunctions =
                        new LinkedHashMap<>(functions);
                Map<String, TriggerExtension.ActionHandler> candidateActions =
                        new LinkedHashMap<>(actions);
                register(provider.get(), candidateDescriptors, candidateFunctions, candidateActions);
                descriptors.clear();
                descriptors.putAll(candidateDescriptors);
                functions.clear();
                functions.putAll(candidateFunctions);
                actions.clear();
                actions.putAll(candidateActions);
            } catch (RuntimeException | java.util.ServiceConfigurationError failure) {
                diagnostics.add(provider.type().getName() + ": " + safeMessage(failure));
            }
        }
        return snapshot(descriptors, functions, actions, diagnostics);
    }

    static void register(
            TriggerExtension extension,
            Map<String, TriggerExtension.Descriptor> descriptors) {
        register(extension, descriptors, new LinkedHashMap<>(), new LinkedHashMap<>());
    }

    static void register(
            TriggerExtension extension,
            Map<String, TriggerExtension.Descriptor> descriptors,
            Map<String, TriggerExtension.FunctionHandler> functions,
            Map<String, TriggerExtension.ActionHandler> actions) {
        Objects.requireNonNull(extension, "extension");
        String namespace = identifier(extension.namespace(), "extension namespace");
        String version = Objects.requireNonNull(extension.version(), "extension version").strip();
        if (!version.matches("0|[1-9]\\d*(?:\\.(?:0|[1-9]\\d*)){2}(?:-[0-9A-Za-z.-]+)?")) {
            throw new IllegalArgumentException("extension version must be semantic: " + version);
        }
        Collection<TriggerExtension.Descriptor> contributions = Objects.requireNonNull(
                extension.descriptors(), "extension descriptors");
        if (contributions.size() > 1_024) throw new IllegalArgumentException("extension has too many descriptors");
        for (TriggerExtension.Descriptor contribution : contributions) {
            Objects.requireNonNull(contribution, "extension descriptor");
            validateTransportValue(contribution.metadata(), 0);
            TriggerExtension.Descriptor existing = descriptors.get(key(contribution));
            if (existing != null) {
                if (existing.kind() != contribution.kind()) {
                    throw new IllegalArgumentException("extension cannot change descriptor kind: "
                            + contribution.id());
                }
                TriggerExtension.Risk raised = TriggerExtension.Risk.atLeast(
                        existing.risk(), contribution.risk());
                if (raised != existing.risk()) {
                    Map<String, Object> metadata = new LinkedHashMap<>(existing.metadata());
                    metadata.put("riskRaisedBy", namespace + '@' + version);
                    descriptors.put(key(existing), new TriggerExtension.Descriptor(
                            existing.id(), existing.kind(), existing.displayName(), existing.description(),
                            existing.parameters(), existing.returnType(), existing.purity(),
                            existing.threadAffinity(), raised, existing.supportedPlatforms(),
                            existing.applicableEvents(), metadata));
                }
                continue;
            }
            if (!contribution.id().startsWith(namespace + ".")
                    && !contribution.id().startsWith(namespace + ":")) {
                throw new IllegalArgumentException("extension descriptor must use namespace " + namespace);
            }
            descriptors.put(key(contribution), contribution);
        }
        Map<String, TriggerExtension.FunctionHandler> contributedFunctions = Map.copyOf(
                Objects.requireNonNull(extension.functionHandlers(), "extension function handlers"));
        Map<String, TriggerExtension.ActionHandler> contributedActions = Map.copyOf(
                Objects.requireNonNull(extension.actionHandlers(), "extension action handlers"));
        contributedFunctions.forEach((id, handler) -> {
            String normalized = capabilityIdentifier(id, namespace);
            TriggerExtension.Descriptor descriptor = descriptors.get("VALUE_FUNCTION:" + normalized);
            if (descriptor == null) descriptor = descriptors.get("CONDITION_FUNCTION:" + normalized);
            if (descriptor == null) {
                throw new IllegalArgumentException("extension function handler has no descriptor: " + normalized);
            }
            if (descriptor.threadAffinity() != TriggerExtension.ThreadAffinity.BACKGROUND
                    || descriptor.purity() == TriggerExtension.Purity.SIDE_EFFECT) {
                throw new IllegalArgumentException("extension function handler must be background and side-effect free");
            }
            if (functions.putIfAbsent(normalized, Objects.requireNonNull(handler, "function handler")) != null) {
                throw new IllegalArgumentException("duplicate extension function handler: " + normalized);
            }
        });
        contributedActions.forEach((id, handler) -> {
            String normalized = capabilityIdentifier(id, namespace);
            TriggerExtension.Descriptor descriptor = descriptors.get("ACTION:" + normalized);
            if (descriptor == null) {
                throw new IllegalArgumentException("extension action handler has no descriptor: " + normalized);
            }
            if (descriptor.threadAffinity() != TriggerExtension.ThreadAffinity.SERVER
                    || descriptor.purity() != TriggerExtension.Purity.SIDE_EFFECT) {
                throw new IllegalArgumentException("extension action handler must be a server-thread side effect");
            }
            if (actions.putIfAbsent(normalized, Objects.requireNonNull(handler, "action handler")) != null) {
                throw new IllegalArgumentException("duplicate extension action handler: " + normalized);
            }
        });
    }

    private static Snapshot snapshot(
            Map<String, TriggerExtension.Descriptor> descriptors,
            Map<String, TriggerExtension.FunctionHandler> functions,
            Map<String, TriggerExtension.ActionHandler> actions,
            List<String> diagnostics) {
        List<TriggerExtension.Descriptor> ordered = descriptors.values().stream()
                .sorted(Comparator.comparing((TriggerExtension.Descriptor value) -> value.kind().name())
                        .thenComparing(TriggerExtension.Descriptor::id)).toList();
        long revision = 0xcbf29ce484222325L;
        for (TriggerExtension.Descriptor descriptor : ordered) {
            String signature = descriptor.id() + '|' + descriptor.kind() + '|' + descriptor.returnType()
                    + '|' + descriptor.purity() + '|' + descriptor.threadAffinity() + '|' + descriptor.risk()
                    + '|' + descriptor.supportedPlatforms().stream().sorted().toList()
                    + '|' + descriptor.applicableEvents().stream().sorted().toList()
                    + '|' + descriptor.parameters();
            for (int index = 0; index < signature.length(); index++) {
                revision ^= signature.charAt(index);
                revision *= 0x100000001b3L;
            }
        }
        return new Snapshot(ordered, List.copyOf(diagnostics), Map.copyOf(functions), Map.copyOf(actions),
                Math.max(1L, revision & Long.MAX_VALUE));
    }

    static void validateTransportValue(Object value, int depth) {
        if (depth > 6) throw new IllegalArgumentException("extension metadata is nested too deeply");
        if (value == null || value instanceof String || value instanceof Boolean) return;
        if (value instanceof Number number) {
            if (!Double.isFinite(number.doubleValue())) {
                throw new IllegalArgumentException("extension metadata contains a non-finite number");
            }
            return;
        }
        if (value instanceof Map<?, ?> map) {
            if (map.size() > 256) throw new IllegalArgumentException("extension metadata map is too large");
            map.forEach((key, entry) -> {
                if (!(key instanceof String)) {
                    throw new IllegalArgumentException("extension metadata keys must be strings");
                }
                validateTransportValue(entry, depth + 1);
            });
            return;
        }
        if (value instanceof Collection<?> values) {
            if (values.size() > 256) throw new IllegalArgumentException("extension metadata list is too large");
            values.forEach(entry -> validateTransportValue(entry, depth + 1));
            return;
        }
        throw new IllegalArgumentException("extension metadata is not transport-safe: "
                + value.getClass().getSimpleName());
    }

    private static String identifier(String value, String field) {
        String normalized = Objects.requireNonNull(value, field).strip().toLowerCase(Locale.ROOT);
        if (!normalized.matches("[a-z][a-z0-9_-]{0,63}")) {
            throw new IllegalArgumentException(field + " contains unsupported characters");
        }
        return normalized;
    }

    private static String capabilityIdentifier(String value, String namespace) {
        String normalized = Objects.requireNonNull(value, "handler id").strip().toLowerCase(Locale.ROOT);
        if (!normalized.matches("[a-z][a-z0-9_.:_-]{0,127}")) {
            throw new IllegalArgumentException("handler id contains unsupported characters");
        }
        if (!normalized.startsWith(namespace + ".") && !normalized.startsWith(namespace + ":")) {
            throw new IllegalArgumentException("extension handler must use namespace " + namespace);
        }
        return normalized;
    }

    private static String safeMessage(Throwable failure) {
        return failure.getMessage() == null ? failure.getClass().getSimpleName() : failure.getMessage();
    }

    private static String key(TriggerExtension.Descriptor descriptor) {
        return descriptor.kind() + ":" + descriptor.id();
    }

    public record Snapshot(
            List<TriggerExtension.Descriptor> descriptors,
            List<String> diagnostics,
            Map<String, TriggerExtension.FunctionHandler> functionHandlers,
            Map<String, TriggerExtension.ActionHandler> actionHandlers,
            long catalogRevision) {
        public Snapshot {
            descriptors = List.copyOf(descriptors);
            diagnostics = List.copyOf(diagnostics);
            functionHandlers = Map.copyOf(functionHandlers);
            actionHandlers = Map.copyOf(actionHandlers);
        }
    }
}