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

io.vertx.spi.cluster.hazelcast.impl.SubsMapHelper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Red Hat, Inc.
 *
 * Red Hat 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 io.vertx.spi.cluster.hazelcast.impl;

import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.MapEvent;
import com.hazelcast.multimap.MultiMap;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.core.spi.cluster.RegistrationInfo;
import io.vertx.core.spi.cluster.RegistrationListener;
import io.vertx.core.spi.cluster.RegistrationUpdateEvent;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author Thomas Segismont
 */
public class SubsMapHelper implements EntryListener {

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

  private final Throttling throttling;
  private final MultiMap map;
  private final RegistrationListener registrationListener;
  private final UUID listenerId;

  private final ConcurrentMap> ownSubs = new ConcurrentHashMap<>();
  private final ConcurrentMap> localSubs = new ConcurrentHashMap<>();
  private final ReadWriteLock republishLock = new ReentrantReadWriteLock();

  public SubsMapHelper(HazelcastInstance hazelcast, RegistrationListener registrationListener) {
    throttling = new Throttling(this::getAndUpdate);
    map = hazelcast.getMultiMap("__vertx.subs");
    this.registrationListener = registrationListener;
    listenerId = map.addEntryListener(this, false);
  }

  public List get(String address) {
    Lock readLock = republishLock.readLock();
    readLock.lock();
    try {
      List list;
      int size;
      Collection remote = map.get(address);
      size = remote.size();
      Set local = localSubs.get(address);
      if (local != null) {
        synchronized (local) {
          size += local.size();
          if (size == 0) {
            return Collections.emptyList();
          }
          list = new ArrayList<>(size);
          list.addAll(local);
        }
      } else if (size == 0) {
        return Collections.emptyList();
      } else {
        list = new ArrayList<>(size);
      }
      for (HazelcastRegistrationInfo hazelcastRegistrationInfo : remote) {
        RegistrationInfo unwrap = hazelcastRegistrationInfo.unwrap();
        list.add(unwrap);
      }
      return list;
    } finally {
      readLock.unlock();
    }
  }

  public void put(String address, RegistrationInfo registrationInfo) {
    Lock readLock = republishLock.readLock();
    readLock.lock();
    try {
      if (registrationInfo.localOnly()) {
        localSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr));
        fireRegistrationUpdateEvent(address);
      } else {
        ownSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr));
        map.put(address, wrapRegistrationInfo(registrationInfo));
      }
    } finally {
      readLock.unlock();
    }
  }

  private HazelcastRegistrationInfo wrapRegistrationInfo(RegistrationInfo registrationInfo) {
    return new HazelcastRegistrationInfo(registrationInfo);
  }

  private Set addToSet(RegistrationInfo registrationInfo, Set curr) {
    Set res = curr != null ? curr : Collections.synchronizedSet(new LinkedHashSet<>());
    res.add(registrationInfo);
    return res;
  }

  public void remove(String address, RegistrationInfo registrationInfo) {
    Lock readLock = republishLock.readLock();
    readLock.lock();
    try {
      if (registrationInfo.localOnly()) {
        localSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr));
        fireRegistrationUpdateEvent(address);
      } else {
        ownSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr));
        map.remove(address, wrapRegistrationInfo(registrationInfo));
      }
    } finally {
      readLock.unlock();
    }
  }

  private Set removeFromSet(RegistrationInfo registrationInfo, Set curr) {
    curr.remove(registrationInfo);
    return curr.isEmpty() ? null : curr;
  }

  public void removeAllForNodes(Set nodeIds) {
    for (Map.Entry entry : map.entrySet()) {
      HazelcastRegistrationInfo registrationInfo = entry.getValue();
      if (nodeIds.contains(registrationInfo.unwrap().nodeId())) {
        map.remove(entry.getKey(), registrationInfo);
      }
    }
  }

  public void republishOwnSubs() {
    Lock writeLock = republishLock.writeLock();
    writeLock.lock();
    try {
      for (Map.Entry> entry : ownSubs.entrySet()) {
        String address = entry.getKey();
        for (RegistrationInfo registrationInfo : entry.getValue()) {
          map.put(address, wrapRegistrationInfo(registrationInfo));
        }
      }
    } finally {
      writeLock.unlock();
    }
  }

  @Override
  public void entryAdded(EntryEvent event) {
    fireRegistrationUpdateEvent(event.getKey());
  }

  private void fireRegistrationUpdateEvent(String address) {
    throttling.onEvent(address);
  }

  private void getAndUpdate(String address) {
    if (registrationListener.wantsUpdatesFor(address)) {
      List registrationInfos;
      try {
        registrationInfos = get(address);
      } catch (Exception e) {
        log.trace("A failure occurred while retrieving the updated registrations", e);
        registrationInfos = Collections.emptyList();
      }
      registrationListener.registrationsUpdated(new RegistrationUpdateEvent(address, registrationInfos));
    }
  }

  @Override
  public void entryEvicted(EntryEvent event) {
  }

  @Override
  public void entryRemoved(EntryEvent event) {
    fireRegistrationUpdateEvent(event.getKey());
  }

  @Override
  public void entryUpdated(EntryEvent event) {
    fireRegistrationUpdateEvent(event.getKey());
  }

  @Override
  public void mapCleared(MapEvent event) {
  }

  @Override
  public void mapEvicted(MapEvent event) {
  }

  @Override
  public void entryExpired(EntryEvent event) {
  }

  public void close() {
    map.removeEntryListener(listenerId);
    throttling.close();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy