All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.ponfee.disjob.registry.ServerRegistry Maven / Gradle / Ivy

/*
 * Copyright 2022-2024 Ponfee (http://www.ponfee.cn/)
 *
 * 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
 *
 *     https://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 cn.ponfee.disjob.registry;

import cn.ponfee.disjob.common.concurrent.TripState;
import cn.ponfee.disjob.common.util.GenericUtils;
import cn.ponfee.disjob.core.base.RegistryEventType;
import cn.ponfee.disjob.core.base.Server;
import cn.ponfee.disjob.registry.discovery.ServerDiscovery;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Registry and discovery server.
 *
 * @param  the registry server type
 * @param  the discovery server type
 * @author Ponfee
 */
public abstract class ServerRegistry implements Registry, Discovery {
    protected final Logger log = LoggerFactory.getLogger(getClass());

    protected final char separator;

    protected final ServerRole registryRole;
    protected final String registryRootPath;

    protected final ServerRole discoveryRole;
    protected final String discoveryRootPath;
    private final ServerDiscovery serverDiscovery;

    /**
     * Registered servers.
     */
    protected final Set registered = ConcurrentHashMap.newKeySet();

    /**
     * Server registry state
     */
    protected final TripState state = TripState.createStarted();

    protected ServerRegistry(AbstractRegistryProperties config, RestTemplate restTemplate, char separator) {
        this.separator = separator;

        String prefix = prune(config.getNamespace(), separator);

        this.registryRole = ServerRole.of(GenericUtils.getActualTypeArgument(getClass(), 0));
        this.registryRootPath = prefix + registryRole.key();

        this.discoveryRole = ServerRole.of(GenericUtils.getActualTypeArgument(getClass(), 1));
        this.discoveryRootPath = prefix + discoveryRole.key();

        this.serverDiscovery = ServerDiscovery.of(discoveryRole, restTemplate);
    }

    /**
     * Returns is connected registry center.
     *
     * @return {@code true} if available
     */
    public abstract boolean isConnected();

    @Override
    public final List getDiscoveredServers(String group) {
        return serverDiscovery.getServers(group);
    }

    @Override
    public final boolean hasDiscoveredServers() {
        return serverDiscovery.hasServers();
    }

    @Override
    public final boolean isDiscoveredServer(D server) {
        return serverDiscovery.isAlive(server);
    }

    /**
     * Close registry.
     */
    @Override
    public void close() {
        registered.clear();
    }

    @Override
    public final ServerRole registryRole() {
        return registryRole;
    }

    @Override
    public final ServerRole discoveryRole() {
        return discoveryRole;
    }

    protected void publishServerChanged(RegistryEventType eventType, R rServer) {
        log.info("Publish server changed: {}, {}", eventType, rServer);
        serverDiscovery.notifyServers(eventType, rServer);
    }

    @Override
    public final void subscribeServerChanged(RegistryEventType eventType, D dServer) {
        log.info("Subscribe server changed: {}, {}", eventType, dServer);
        serverDiscovery.updateServers(eventType, dServer);
    }

    /**
     * Refresh discovery servers.
     *
     * @param list the list
     */
    protected final void refreshDiscoveryServers(List list) {
        List servers = deserializeServers(list, discoveryRole);
        serverDiscovery.refreshServers(servers);
        if (servers.isEmpty()) {
            log.warn("Not discovered available {}", discoveryRole);
        }
    }

    protected final  List deserializeServers(List list, ServerRole serverRole) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list.stream()
            .map(e -> deserializeServer(e, serverRole))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }

    protected final  S deserializeServer(String server, ServerRole serverRole) {
        if (StringUtils.isBlank(server)) {
            return null;
        }
        try {
            return serverRole.deserialize(server);
        } catch (Throwable t) {
            log.error("Deserialize server failed: {}, {}, {}", serverRole, server, t.getMessage());
            return null;
        }
    }

    // -------------------------------------------------------------------------------------private method

    private static String prune(String namespace, char separator) {
        if (StringUtils.isBlank(namespace)) {
            return "";
        }
        if (namespace.contains(String.valueOf(separator))) {
            throw new IllegalArgumentException("Namespace cannot contains separator symbol '" + separator + "'");
        }
        return namespace.trim() + separator;
    }

}