org.apache.usergrid.persistence.actorsystem.ClientActor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.usergrid.persistence.actorsystem.ClientActor.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.usergrid.persistence.actorsystem;

import akka.actor.ActorSelection;
import akka.actor.Address;
import akka.actor.UntypedActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent;
import akka.cluster.Member;
import akka.cluster.MemberStatus;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Once notified of nodes, sends unique propertyValue requests to ClusterSingletonRouter via it's local proxy.
 */
public class ClientActor extends UntypedActor {
    private static final Logger logger = LoggerFactory.getLogger(ClientActor.class);

    private final String name = RandomStringUtils.randomAlphanumeric(4);

    private final Set<Address> nodes = new HashSet<>();
    private final Cluster cluster = Cluster.get(getContext().system());
    private final Map<Class, String> routersByMessageType;

    private boolean ready = false;

    public ClientActor(Map<Class, String> routersByMessageType) {
        this.routersByMessageType = routersByMessageType;
    }

    // subscribe to cluster changes, MemberEvent
    @Override
    public void preStart() {
        logger.debug("{} role {} address {}:{} starting up, subscribing to cluster events...", name,
                cluster.getSelfRoles().iterator().next(), cluster.readView().selfAddress().host(),
                cluster.readView().selfAddress().hostPort());
        cluster.subscribe(getSelf(), ClusterEvent.MemberEvent.class, ClusterEvent.ReachabilityEvent.class);
    }

    // re-subscribe when restart
    @Override
    public void postStop() {
        cluster.unsubscribe(getSelf());
    }

    @Override
    public void onReceive(Object message) {

        int startSize = nodes.size();

        String routerPath = routersByMessageType.get(message.getClass());

        if (routerPath != null && ready) {

            // just pick any node, the ClusterSingletonRouter will do the consistent hash routing
            List<Address> nodesList = new ArrayList<>(nodes);
            Address address = nodesList.get(ThreadLocalRandom.current().nextInt(nodesList.size()));
            ActorSelection service = getContext().actorSelection(address + routerPath);
            service.tell(message, getSender());

        } else if (routerPath != null && !ready) {

            logger.debug("{} responding with status unknown", name);
            getSender().tell(new ErrorResponse("ClientActor not ready"), getSender());

        } else if (message instanceof StatusRequest) {
            if (ready) {
                getSender().tell(new StatusMessage(name, StatusMessage.Status.READY), getSender());
            } else {
                getSender().tell(new StatusMessage(name, StatusMessage.Status.INITIALIZING), getSender());
            }
            return;

        } else {
            processAsClusterEvent(message);
        }

        if (logger.isDebugEnabled() && startSize != nodes.size()) {
            logger.debug("{} now knows {} nodes", name, nodes.size());
        }

        if (!nodes.isEmpty() && !ready) {
            logger.debug(name + " is ready");
            ready = true;

        } else if (nodes.isEmpty() && ready) {
            ready = false;
        }
    }

    /**
     * Process messages about nodes up, down, reachable and unreachable.
     */
    private void processAsClusterEvent(Object message) {

        if (message instanceof ClusterEvent.CurrentClusterState) {
            ClusterEvent.CurrentClusterState state = (ClusterEvent.CurrentClusterState) message;
            nodes.clear();
            for (Member member : state.getMembers()) {
                if (member.hasRole("io") && member.status().equals(MemberStatus.up())) {
                    nodes.add(member.address());
                    logger.debug("RequestActor {} received cluster-state member-up for {}", name, member.address());
                }
            }

        } else if (message instanceof ClusterEvent.MemberUp) {
            ClusterEvent.MemberUp mUp = (ClusterEvent.MemberUp) message;
            if (mUp.member().hasRole("io")) {
                nodes.add(mUp.member().address());
            }
            logger.debug("{} received member-up for {}", name, mUp.member().address());

        } else if (message instanceof ClusterEvent.MemberEvent) {
            ClusterEvent.MemberEvent other = (ClusterEvent.MemberEvent) message;
            nodes.remove(other.member().address());

        } else if (message instanceof ClusterEvent.UnreachableMember) {
            ClusterEvent.UnreachableMember unreachable = (ClusterEvent.UnreachableMember) message;
            nodes.remove(unreachable.member().address());
            logger.debug("{} received un-reachable for {}", name, unreachable.member().address());

        } else if (message instanceof ClusterEvent.ReachableMember) {
            ClusterEvent.ReachableMember reachable = (ClusterEvent.ReachableMember) message;
            if (reachable.member().hasRole("io")) {
                nodes.add(reachable.member().address());
            }
            logger.debug("{} received reachable for {}", name, reachable.member().address());

        } else {
            logger.error("{}: unhandled message: {}", name, message.toString());
            unhandled(message);
        }
    }

    /**
     * RequestAction responds to StatusRequests.
     */
    public static class StatusRequest implements Serializable {
    }

    /**
     * RequestActor responds with, and some times unilaterally sends StatusMessages.
     */
    public static class StatusMessage implements Serializable {
        final String name;

        public Status getStatus() {
            return status;
        }

        public enum Status {
            INITIALIZING, READY
        }

        private final Status status;

        public StatusMessage(String name, Status status) {
            this.name = name;
            this.status = status;
        }

        public String getName() {
            return name;
        }

        public boolean isReady() {
            return status.equals(Status.READY);
        }
    }

    public static class ErrorResponse implements Serializable {
        private String message;

        public ErrorResponse(String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }
    }

}