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

org.jgroups.relay_server.RelayService Maven / Gradle / Ivy

The newest version!
package org.jgroups.relay_server;

import io.grpc.stub.StreamObserver;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Bela Ban
 * @since  1.0.0
 * todo: Add logging instead of System.err.printf
 */
public class RelayService extends RelayServiceGrpc.RelayServiceImplBase {
    // protected final Collection>    observers=new ConcurrentLinkedQueue<>();
    protected final ConcurrentMap members=new ConcurrentHashMap<>();
    protected long                                        view_id=0; // global, for all clusters, but who cares

    @Override
    public StreamObserver connect(StreamObserver responseObserver) {
        return new StreamObserver() {
            public void onNext(Request req) {
                if(req.hasMessage()) {
                    handleMessage(req.getMessage());
                    return;
                }
                if(req.hasJoinReq()) {
                    handleJoinRequest(req.getJoinReq(), responseObserver);
                    return;
                }
                throw new IllegalStateException(String.format("request is illegal: %s", req));
            }

            public void onError(Throwable t) {
                remove(responseObserver);
            }

            public void onCompleted() {
                remove(responseObserver);
            }
        };
    }




    @Override
    public void leave(LeaveRequest req, StreamObserver responseObserver) {
        final String  cluster=req.getClusterName();
        boolean       removed=false;
        Address       leaver=req.getLeaver();

        if(leaver == null)
            return;

        SynchronizedMap m=members.get(cluster);
        if(m != null) {
            Map> map=m.getMap();
            Lock lock=m.getLock();
            lock.lock();
            try {
                StreamObserver observer=map.remove(leaver);
                if(observer != null) {
                    removed=true;
                    observer.onCompleted();
                }
                if(removed)
                    postView(map);
            }
            finally {
                lock.unlock();
            }
        }
        responseObserver.onNext(Void.newBuilder().build());
        responseObserver.onCompleted();
    }

    @Override
    public void dump(Void request, StreamObserver responseObserver) {
        String result=dumpDiagnostics();
        responseObserver.onNext(DumpResponse.newBuilder().setDump(result).build());
        responseObserver.onCompleted();
    }


    protected void remove(StreamObserver observer) {
        if(observer == null)
            return;

        for(Map.Entry entry : members.entrySet()) {
            String cluster=entry.getKey();
            SynchronizedMap m=entry.getValue();
            Map> map=m.getMap();
            Lock lock=m.getLock();
            lock.lock();
            try {
                map.values().removeIf(val -> Objects.equals(val, observer));
                if(map.isEmpty())
                    members.remove(cluster);
                else
                    postView(map);
            }
            finally {
                lock.unlock();
            }
        }
    }

    protected void handleJoinRequest(JoinRequest join_req, StreamObserver responseObserver) {
        final String  cluster=join_req.getClusterName();
        final Address joiner=join_req.getAddress();

        SynchronizedMap m=members.computeIfAbsent(cluster, k -> new SynchronizedMap(new LinkedHashMap()));
        Map> map=m.getMap();
        Lock lock=m.getLock();
        lock.lock();
        try {
            if(map.putIfAbsent(joiner, responseObserver) == null)
                postView(map);
        }
        finally {
            lock.unlock();
        }
    }

    protected void handleMessage(Message msg) {
        String cluster=msg.getClusterName();
        Address dest=msg.hasDestination()? msg.getDestination() : null;

        SynchronizedMap mbrs=members.get(cluster);
        if(mbrs == null) {
            System.err.printf("no members found for cluster %s\n", cluster);
            return;
        }

        if(dest == null)
            relayToAll(msg, mbrs);
        else
            relayTo(msg, mbrs);
    }


    protected void relayToAll(Message msg, SynchronizedMap m) {
        Map> map=m.getMap();
        Lock lock=m.getLock();
        lock.lock();
        try {
            if(!map.isEmpty()) {
                System.out.printf("-- relaying msg to %d members for cluster %s\n", map.size(), msg.getClusterName());
                Response response=Response.newBuilder().setMessage(msg).build();
                for(StreamObserver obs: map.values()) {
                    try {
                        obs.onNext(response);
                    }
                    catch(Throwable t) {
                        System.out.printf("exception relaying message (removing observer): %s\n", t);
                        remove(obs);
                    }
                }
            }
        }
        finally {
            lock.unlock();
        }
    }

    protected void relayTo(Message msg, SynchronizedMap m) {
        Address dest=msg.getDestination();
        Map> map=m.getMap();
        Lock lock=m.getLock();
        lock.lock();
        try {
            StreamObserver obs=map.get(dest);
            if(obs == null) {
                System.err.printf("unicast destination %s not found; dropping message\n", dest.getName());
                return;
            }

            System.out.printf("-- relaying msg to member %s for cluster %s\n", dest.getName(), msg.getClusterName());
            Response response=Response.newBuilder().setMessage(msg).build();
            try {
                obs.onNext(response);
            }
            catch(Throwable t) {
                System.out.printf("exception relaying message to %s (removing observer): %s\n", dest.getName(), t);
                remove(obs);
            }
        }
        finally {
            lock.unlock();
        }
    }


    protected void postView(Map> map) {
        View.Builder view_builder=View.newBuilder();
        Address coord=null;
        for(Address mbr: map.keySet()) {
            view_builder.addMember(mbr);
            if(coord == null)
                coord=mbr;
        }
        view_builder.setViewId(ViewId.newBuilder().setCreator(coord).setId(getNewViewId()).build());

        View new_view=view_builder.build();
        Response response=Response.newBuilder().setView(new_view).build();

        for(Iterator>> it=map.entrySet().iterator(); it.hasNext();) {
            Map.Entry> entry=it.next();
            StreamObserver                    val=entry.getValue();
            try {
                val.onNext(response);
            }
            catch(Throwable t) {
                it.remove();
            }
        }
    }

    protected String dumpDiagnostics() {
        StringBuilder sb=new StringBuilder();
        sb.append("members:\n");
        dumpViews(sb);
        return sb.append("\n").toString();
    }

    protected void dumpViews(final StringBuilder sb) {
        for(Map.Entry entry: members.entrySet()) {
            String cluster=entry.getKey();
            SynchronizedMap m=entry.getValue();
            Map> map=m.getMap();
            Lock lock=m.getLock();
            lock.lock();
            try {
                sb.append(cluster).append(": ").append(Utils.print(map.keySet())).append("\n");
            }
            finally {
                lock.unlock();
            }
        }
    }

    protected synchronized long getNewViewId() {return view_id++;}


    protected static class SynchronizedMap {
        protected final Map> map;
        protected final Lock                                  lock=new ReentrantLock();

        public SynchronizedMap(Map> map) {
            this.map=map;
        }

        protected Map> getMap()       {return map;}
        protected Lock                                  getLock()      {return lock;}
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy