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

io.jsync.spi.cluster.impl.HazelcastClusterManager Maven / Gradle / Ivy

There is a newer version: 1.10.13
Show newest version
/*
 * Copyright (c) 2011-2013 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.jsync.spi.cluster.impl;

import com.hazelcast.client.proxy.ClientClusterProxy;
import com.hazelcast.config.Config;
import com.hazelcast.config.InMemoryXmlConfig;
import com.hazelcast.config.MultiMapConfig;
import com.hazelcast.core.*;
import io.jsync.logging.Logger;
import io.jsync.logging.impl.LoggerFactory;
import io.jsync.spi.AsyncSPI;
import io.jsync.spi.cluster.AsyncMap;
import io.jsync.spi.cluster.AsyncMultiMap;
import io.jsync.spi.cluster.ClusterManager;
import io.jsync.spi.cluster.NodeListener;
import io.jsync.utils.CryptoUtils;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.util.*;

/**
 * A cluster manager that uses Hazelcast
 *
 * @author Tim Fox
 */
public class HazelcastClusterManager implements ClusterManager, MembershipListener {

    private static final Logger log = LoggerFactory.getLogger(HazelcastClusterManager.class);

    // Hazelcast config file
    private static final String DEFAULT_CONFIG_FILE = "cluster.xml";
    private static final String CONFIG_FILE = "cluster.xml";

    private static HazelcastInstance nextHazelcastInstance;
    private final AsyncSPI async;
    private HazelcastInstance hazelcast;
    private String nodeID;
    private NodeListener nodeListener;
    private boolean active;
    private String registrationID;
    /**
     * Constructor
     */
    protected HazelcastClusterManager(final AsyncSPI async) {
        this.async = async;
        // We have our own shutdown hook and need to ensure ours runs before Hazelcast is shutdown
        System.setProperty("hazelcast.shutdownhook.enabled", "false");
    }

    public static void setNextInstance(HazelcastInstance nextInstance) {
        nextHazelcastInstance = nextInstance;
    }

    private static InputStream getConfigStream() {
        ClassLoader cl = HazelcastClusterManager.class.getClassLoader();
        InputStream is = cl.getResourceAsStream(CONFIG_FILE);
        // Check for CONFIG_FILE in jar location
        File curDir = (new File("."));
        if (curDir.isDirectory()) {
            File[] files = curDir.listFiles();
            for (File file : files) {
                if (file.getName().equals(CONFIG_FILE)) {
                    try {
                        log.info("Loading cluster config from " + CONFIG_FILE);
                        return new FileInputStream(file);
                    } catch (FileNotFoundException e) {
                        continue; // Not Found so continue? Why not
                    }
                }
            }
        }

        if (is == null) {
            is = cl.getResourceAsStream(DEFAULT_CONFIG_FILE);
        }
        return is;
    }

    /**
     * Get the Hazelcast config
     *
     * @return a config object
     */
    private static String getConfigXml() {
        try (InputStream is = getConfigStream()) {
            if (is != null) {
                return IOUtils.toString(is, "UTF-8");
            }
        } catch (IOException ex) {
            log.error("Failed to read config", ex);
        }
        return null;
    }

    public static Config getDefaultConfig() {

        // Right now we want to load the getConfig() into a InMemoryXmlConfig with some default properties
        Properties defaultConfigProperties = new Properties();
        defaultConfigProperties.put("hazelcast.memcache.enabled", false);
        defaultConfigProperties.put("hazelcast.rest.enabled", false);
        defaultConfigProperties.put("hazelcast.wait.seconds.before.join", 0);
        defaultConfigProperties.put("hazelcast.logging.type", false);

        Config cfg = new InMemoryXmlConfig(getConfigXml(), defaultConfigProperties);

        // Now we want to configure some other stuff by default
        MultiMapConfig subsConfig = new MultiMapConfig("subs");
        subsConfig.setBinary(true);
        subsConfig.setAsyncBackupCount(2);
        subsConfig.setBackupCount(1);

        cfg.addMultiMapConfig(subsConfig);
        return cfg;
    }

    /**
     * This method joins the cluster. If the system property hazelcast.mode is set to "client"
     * then this hazelcast instance will connect via a HazelcastClient rather than a direct member
     * of the cluster. This can be set via
     */
    public synchronized void join() {
        if (active) {
            return;
        }

        if (nextHazelcastInstance == null) {
            hazelcast = Hazelcast.newHazelcastInstance(getConfig());
            nodeID = hazelcast.getCluster().getLocalMember().getUuid();
        } else {
            hazelcast = nextHazelcastInstance;
            // Ensure that it's not a ClusterClient
            if (!(hazelcast.getCluster() instanceof ClientClusterProxy)) {
                nodeID = hazelcast.getCluster().getLocalMember().getUuid();
            } else {
                // Generate a random ID
                // Not sure if this is too safe
                nodeID = "client-" + CryptoUtils.calculateHmacSHA1(UUID.randomUUID().toString(), UUID.randomUUID().toString());
            }
            nextHazelcastInstance = null;
        }

        registrationID = hazelcast.getCluster().addMembershipListener(this);
        active = true;
    }

    /**
     * Every eventbus handler has an ID. SubsMap (subscriber map) is a MultiMap which
     * maps handler-IDs with server-IDs and thus allows the eventbus to determine where
     * to send messages.
     *
     * @param name A unique name by which the the MultiMap can be identified within the cluster.
     *             See the cluster config file (e.g. cluster.xml in case of HazelcastClusterManager) for
     *             additional MultiMap config parameters.
     * @return subscription map
     */
    public  AsyncMultiMap getAsyncMultiMap(final String name) {
        com.hazelcast.core.MultiMap map = hazelcast.getMultiMap(name);
        return new HazelcastAsyncMultiMap(async, map);
    }

    @Override
    public String getNodeID() {
        return nodeID;
    }

    @Override
    public List getNodes() {
        Set members = hazelcast.getCluster().getMembers();
        List lMembers = new ArrayList<>();
        for (Member member : members) {
            lMembers.add(member.getUuid());
        }
        return lMembers;
    }

    @Override
    public void nodeListener(NodeListener listener) {
        this.nodeListener = listener;
    }

    @Override
    public  AsyncMap getAsyncMap(String name) {
        IMap map = hazelcast.getMap(name);
        return new HazelcastAsyncMap(async, map);
    }

    @Override
    public  Map getSyncMap(String name) {
        IMap map = hazelcast.getMap(name);
        return map;
    }

    public synchronized void leave() {
        if (!active) {
            return;
        }
        if (hazelcast.getLifecycleService().isRunning()) {
            hazelcast.getCluster().removeMembershipListener(registrationID);
            hazelcast.getLifecycleService().shutdown();
        }
        active = false;
    }

    @Override
    public synchronized void memberAdded(MembershipEvent membershipEvent) {
        if (!active) {
            return;
        }
        try {
            if (nodeListener != null) {
                Member member = membershipEvent.getMember();
                nodeListener.nodeAdded(member.getUuid());
            }
        } catch (Throwable t) {
            log.error("Failed to handle memberAdded", t);
        }
    }

    @Override
    public synchronized void memberRemoved(MembershipEvent membershipEvent) {
        if (!active) {
            return;
        }
        try {
            if (nodeListener != null) {
                Member member = membershipEvent.getMember();
                nodeListener.nodeLeft(member.getUuid());
            }
        } catch (Throwable t) {
            log.error("Failed to handle memberRemoved", t);
        }
    }

    @Override
    public void memberAttributeChanged(MemberAttributeEvent memberAttributeEvent) {
        if (!active) {
            return;
        }
        try {
            if (nodeListener != null) {
                Member member = memberAttributeEvent.getMember();
                nodeListener.nodeAttributeChanged(member.getUuid(),
                        memberAttributeEvent.getKey(), memberAttributeEvent.getValue());
            }
        } catch (Throwable t) {
            log.error("Failed to handle memberAttributeChanged", t);
        }
    }

    protected Config getConfig() {
        return getDefaultConfig();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy