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

org.shoal.ha.group.gms.GroupServiceProvider Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.shoal.ha.group.gms;

import com.sun.enterprise.ee.cms.core.*;
import com.sun.enterprise.ee.cms.impl.client.*;
import com.sun.enterprise.ee.cms.logging.GMSLogDomain;
import org.shoal.ha.cache.impl.util.MessageReceiver;
import org.shoal.ha.group.GroupMemberEventListener;
import org.shoal.ha.group.GroupService;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Mahesh Kannan
 */
public class GroupServiceProvider
        implements GroupService, CallBack {

    private static final Logger logger = GMSLogDomain.getLogger(GMSLogDomain.GMS_LOGGER);

    private String myName;

    private String groupName;

    private Properties configProps = new Properties();

    private GroupManagementService gms;

    private GroupHandle groupHandle;

    private ConcurrentHashMap aliveInstances = new ConcurrentHashMap();

    private List listeners = new ArrayList();

    private boolean createdAndJoinedGMSGroup;

    private AtomicLong previousViewId = new AtomicLong(-100);

    private volatile AliveAndReadyView arView;

    private ConcurrentHashMap lastSendMsgFailNotification = new ConcurrentHashMap();

    public GroupServiceProvider(String myName, String groupName, boolean startGMS) {
        init(myName, groupName, startGMS);
    }

    public void processNotification(Signal notification) {
        boolean isJoin = true;
        if ((notification instanceof JoinedAndReadyNotificationSignal)
            || (notification instanceof FailureNotificationSignal)
            || (notification instanceof PlannedShutdownSignal)) {

            isJoin = notification instanceof JoinedAndReadyNotificationSignal;

            checkAndNotifyAboutCurrentAndPreviousMembers(notification.getMemberToken(), isJoin, true);
        }
    }

    private synchronized void checkAndNotifyAboutCurrentAndPreviousMembers(String memberName, boolean isJoinEvent, boolean triggeredByGMS) {

        SortedSet currentAliveAndReadyMembers = gms.getGroupHandle().getCurrentAliveAndReadyCoreView().getMembers();
        AliveAndReadyView aView = gms.getGroupHandle().getPreviousAliveAndReadyCoreView();
        SortedSet previousAliveAndReadyMembers = new TreeSet();

        if (aView == null) { //Possible during unit tests when listeners are registered before GMS is started
            return;
        }


        long arViewId = aView.getViewId();
        long knownId = previousViewId.get();
        Signal sig = aView.getSignal();

//        System.out.println("**GroupServiceProvider:checkAndNotifyAboutCurrentAndPreviousMembers: previous viewID: " + knownId
//                + "; current viewID: " + arViewId + "; " + aView.getSignal());
        if (knownId < arViewId) {
            if (previousViewId.compareAndSet(knownId, arViewId)) {
                this.arView = aView;
                sig = this.arView.getSignal();
                previousAliveAndReadyMembers = this.arView.getMembers();
            } else {
                previousAliveAndReadyMembers = this.arView.getMembers();
//                System.out.println("**GroupServiceProvider:checkAndNotifyAboutCurrentAndPreviousMembers.  Entered ELSE 1");
            }
        } else {
            previousAliveAndReadyMembers = this.arView.getMembers();
//            System.out.println("**GroupServiceProvider:checkAndNotifyAboutCurrentAndPreviousMembers.  Entered ELSE 2");
        }

        //Listeners must be notified even if view has not changed.
        //This is because this method is called when a listener
        //  is registered
        for (GroupMemberEventListener listener : listeners) {
            listener.onViewChange(memberName, currentAliveAndReadyMembers,
                    previousAliveAndReadyMembers, isJoinEvent);
        }

        if (triggeredByGMS) {
            StringBuilder sb = new StringBuilder("**VIEW: ");
            sb.append("prevViewId: " + knownId).append("; curViewID: ").append(arViewId)
                    .append("; signal: ").append(sig).append(" ");
            sb.append("[current: ");
            String delim = "";
            for (String member : currentAliveAndReadyMembers) {
                sb.append(delim).append(member);
                delim = ", ";
            }
            sb.append("]  [previous: ");
            delim = "";

            for (String member : previousAliveAndReadyMembers) {
                sb.append(delim).append(member);
                delim = ", ";
            }
            sb.append("]");
            logger.log(Level.INFO, sb.toString());
            logger.log(Level.INFO, "**********************************************************************");
        }

    }

    private void init(String myName, String groupName, boolean startGMS) {
        try {
            gms = GMSFactory.getGMSModule(groupName);
        } catch (Exception e) {
            logger.severe("GMS module for group " + groupName + " not enabled");
        }

        if (gms == null) {
            if (startGMS) {
                logger.info("GroupServiceProvider *CREATING* gms module for group " + groupName);
                GroupManagementService.MemberType memberType = myName.startsWith("monitor-")
                        ? GroupManagementService.MemberType.SPECTATOR
                        : GroupManagementService.MemberType.CORE;

                configProps.put(ServiceProviderConfigurationKeys.MULTICASTADDRESS.toString(),
                        System.getProperty("MULTICASTADDRESS", "229.9.1.1"));
                configProps.put(ServiceProviderConfigurationKeys.MULTICASTPORT.toString(), 2299);
                logger.info("Is initial host=" + System.getProperty("IS_INITIAL_HOST"));
                configProps.put(ServiceProviderConfigurationKeys.IS_BOOTSTRAPPING_NODE.toString(),
                        System.getProperty("IS_INITIAL_HOST", "false"));
                if (System.getProperty("INITIAL_HOST_LIST") != null) {
                    configProps.put(ServiceProviderConfigurationKeys.VIRTUAL_MULTICAST_URI_LIST.toString(),
                            myName.equals("DAS"));
                }
                configProps.put(ServiceProviderConfigurationKeys.FAILURE_DETECTION_RETRIES.toString(),
                        System.getProperty("MAX_MISSED_HEARTBEATS", "3"));
                configProps.put(ServiceProviderConfigurationKeys.FAILURE_DETECTION_TIMEOUT.toString(),
                        System.getProperty("HEARTBEAT_FREQUENCY", "2000"));
                // added for junit testing of send and receive to self.
                // these settings are not used in glassfish config of gms anyways.
                configProps.put(ServiceProviderConfigurationKeys.LOOPBACK.toString(), "true");
                final String bindInterfaceAddress = System.getProperty("BIND_INTERFACE_ADDRESS");
                if (bindInterfaceAddress != null) {
                    configProps.put(ServiceProviderConfigurationKeys.BIND_INTERFACE_ADDRESS.toString(), bindInterfaceAddress);
                }

                gms = (GroupManagementService) GMSFactory.startGMSModule(
                        myName, groupName, memberType, configProps);

                createdAndJoinedGMSGroup = true;
            } else {
                logger.fine("**GroupServiceProvider:: Will not start GMS module for group " + groupName + ". It should have been started by now. But GMS: " + gms);
            }
        } else {
            logger.fine("**GroupServiceProvider:: GMS module for group " + groupName + " should have been started by now GMS: " + gms);
        }

        if (gms != null) {
            this.groupHandle = gms.getGroupHandle();
            this.myName = myName;
            this.groupName = groupName;

            gms.addActionFactory(new JoinNotificationActionFactoryImpl(this));
            gms.addActionFactory(new JoinedAndReadyNotificationActionFactoryImpl(this));
            gms.addActionFactory(new FailureNotificationActionFactoryImpl(this));
            gms.addActionFactory(new PlannedShutdownActionFactoryImpl(this));

            logger.info("**GroupServiceProvider:: REGISTERED member event listeners for  => <" + groupName + ", " + myName + ">");

        } else {
            throw new IllegalStateException("GMS has not been started yet for group name: " + groupName + ". Is the cluster up and running");
        }

        if (createdAndJoinedGMSGroup) {
            try {
                gms.join();
                Thread.sleep(3000);
                gms.reportJoinedAndReadyState();
            } catch (Exception ex) {
                logger.log(Level.WARNING, "Got an exception during reportJoinedAndReadyState?", ex);
            }
        }
    }

    public List getCurrentCoreMembers() {
        return groupHandle.getCurrentCoreMembers();
    }

    public void shutdown() {
        //gms.shutdown();
    }

    @Override
    public String getGroupName() {
        return groupName;
    }

    @Override
    public String getMemberName() {
        return myName;
    }

    @Override
    public boolean sendMessage(String targetMemberName, String token, byte[] data) {
        try {
            groupHandle.sendMessage(targetMemberName, token, data);
            return true;
        } catch (MemberNotInViewException memEx) {
            final String msg = "Error during groupHandle.sendMessage(" + targetMemberName + "," +
                                token + ") failed because " + targetMemberName + " is not alive?";
            logSendMsgFailure(memEx, targetMemberName, msg);
        } catch (GMSException gmsEx) {
            try {
                groupHandle.sendMessage(targetMemberName, token, data);
                return true;
            } catch (GMSException gmsEx2) {
                final String msg = "Error during groupHandle.sendMessage(" + targetMemberName + ", " +
                                   token + "; size=" + (data == null ? -1 : data.length) + ")";
                logSendMsgFailure(gmsEx2, targetMemberName, msg);
            }
        }

        return false;
    }

    // ensure that log is not spammed with these messages.
    // package private so can call from junit test
    void logSendMsgFailure(GMSException t, String targetMemberName, String message) {
        final long SEND_FAILED_NOTIFICATION_PERIOD = 1000 * 60 * 60 * 12 ;  // within a 12 hour period,only notify once.

        if (targetMemberName == null) {
            targetMemberName = "";
        }

        final Long lastNotify = lastSendMsgFailNotification.get(targetMemberName);
        final long currentTime = System.currentTimeMillis();
        if (lastNotify == null || currentTime > lastNotify + SEND_FAILED_NOTIFICATION_PERIOD) {
            lastSendMsgFailNotification.put(targetMemberName, new Long(currentTime));
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.WARNING, message, t);
            } else {
                Throwable causeT = t.getCause();
                String cause = causeT == null ? t.getMessage() : causeT.getMessage();
                logger.log(Level.WARNING, message + " Cause:" + cause);
            }
        }
    }

    @Override
    public void registerGroupMessageReceiver(String messageToken, MessageReceiver receiver) {
        logger.fine("[GroupServiceProvider]:  REGISTERED A MESSAGE LISTENER: "
                + receiver + "; for token: " + messageToken);
        gms.addActionFactory(new MessageActionFactoryImpl(receiver), messageToken);
    }

    @Override
    public void registerGroupMemberEventListener(GroupMemberEventListener listener) {
        listeners.add(listener);
        checkAndNotifyAboutCurrentAndPreviousMembers(myName, true, false);
    }

    @Override
    public void removeGroupMemberEventListener(GroupMemberEventListener listener) {
        listeners.remove(listener);
    }

    @Override
    public void close() {
        if (createdAndJoinedGMSGroup) {
            shutdown();
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy