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

io.atomix.collections.internal.MultiMapState Maven / Gradle / Ivy

There is a newer version: 1.0.0-rc8
Show newest version
/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed 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.atomix.collections.internal;

import io.atomix.catalyst.concurrent.Scheduled;
import io.atomix.collections.DistributedMultiMap;
import io.atomix.copycat.server.Commit;
import io.atomix.resource.ResourceStateMachine;

import java.time.Duration;
import java.util.*;

/**
 * Map state machine.
 *
 * @author Jordan Halterman
 */
public class MultiMapState extends ResourceStateMachine {
  private final Map>> map = new HashMap<>();
  private final Map timers = new HashMap<>();
  private final DistributedMultiMap.Order order;

  public MultiMapState(Properties properties) {
    super(properties);
    this.order = DistributedMultiMap.Order.valueOf(config.getProperty("order", DistributedMultiMap.Order.INSERT.name().toLowerCase()).toUpperCase());
  }

  /**
   * Creates a new value map.
   */
  private Map> createValueMap() {
    switch (order) {
      case NONE:
        return new HashMap<>();
      case NATURAL:
        return new TreeMap<>();
      case INSERT:
        return new LinkedHashMap<>();
      default:
        return new HashMap<>();
    }
  }

  /**
   * Handles a contains key commit.
   */
  public boolean containsKey(Commit commit) {
    try {
      return map.containsKey(commit.operation().key());
    } finally {
      commit.close();
    }
  }

  /**
   * Handles a get commit.
   */
  public Collection get(Commit commit) {
    try {
      Map> values = map.get(commit.operation().key());
      if (values == null) {
        return Collections.EMPTY_LIST;
      }

      Collection results = new ArrayList<>(values.size());
      for (Commit value : values.values()) {
        results.add(value.operation().value());
      }
      return results;
    } finally {
      commit.close();
    }
  }

  /**
   * Handles a put commit.
   */
  public boolean put(Commit commit) {
    try {
      Map> values = map.get(commit.operation().key());
      if (values == null) {
        values = createValueMap();
        map.put(commit.operation().key(), values);
      }

      final Map> keyValues = values;
      if (!values.containsKey(commit.operation().value())) {
        Scheduled timer = commit.operation().ttl() > 0 ? executor.schedule(Duration.ofMillis(commit.operation().ttl()), () -> {
          keyValues.remove(commit.operation().value()).close();
        }) : null;
        values.put(commit.operation().value(), commit);
        timers.put(commit.index(), timer);
        return true;
      } else {
        commit.close();
        return false;
      }
    } catch (Exception e) {
      commit.close();
      throw e;
    }
  }

  /**
   * Handles a remove commit.
   */
  public Object remove(Commit commit) {
    try {
      if (commit.operation().value() != null) {
        Map> values = map.get(commit.operation().key());
        if (values == null) {
          return false;
        }

        Commit previous = values.remove(commit.operation().value());
        if (previous == null) {
          return false;
        }

        Scheduled timer = timers.remove(previous.index());
        if (timer != null)
          timer.cancel();

        previous.close();

        if (values.isEmpty())
          map.remove(commit.operation().key());
        return true;
      } else {
        Map> values = map.remove(commit.operation().key());
        if (values != null) {
          Collection results = new ArrayList<>(values.size());
          for (Commit value : values.values()) {
            Scheduled timer = timers.remove(value.index());
            if (timer != null)
              timer.cancel();
            results.add(value.operation().value());
            value.close();
          }
          return results;
        }
        return Collections.EMPTY_LIST;
      }
    } finally {
      commit.close();
    }
  }

  /**
   * Handles a remove value commit.
   */
  public void removeValue(Commit commit) {
    try {
      Iterator>>> outerIterator = map.entrySet().iterator();
      while (outerIterator.hasNext()) {
        Map> map = outerIterator.next().getValue();
        Iterator>> innerIterator = map.entrySet().iterator();
        while (innerIterator.hasNext()) {
          Map.Entry> entry = innerIterator.next();
          if ((entry.getValue().operation().value() == null && commit.operation().value() == null)
            || (entry.getValue().operation().value() != null && commit.operation().value() != null && entry.getValue().operation().value().equals(commit.operation().value()))) {
            Scheduled timer = timers.remove(entry.getValue().index());
            if (timer != null)
              timer.cancel();
            entry.getValue().close();
            innerIterator.remove();
          }
        }

        if (map.isEmpty()) {
          outerIterator.remove();
        }
      }
    } finally {
      commit.close();
    }
  }

  /**
   * Handles a size commit.
   */
  public int size(Commit commit) {
    try {
      if (commit.operation().key() != null) {
        Map> values = map.get(commit.operation().key());
        return values != null ? values.size() : 0;
      } else {
        int size = 0;
        for (Map.Entry>> entry : map.entrySet()) {
          size += entry.getValue().size();
        }
        return size;
      }
    } finally {
      commit.close();
    }
  }

  /**
   * Handles an is empty commit.
   */
  public boolean isEmpty(Commit commit) {
    try {
      return map == null || map.isEmpty();
    } finally {
      commit.close();
    }
  }

  /**
   * Handles a clear commit.
   */
  public void clear(Commit commit) {
    try {
      delete();
    } finally {
      commit.close();
    }
  }

  @Override
  public void delete() {
    Iterator>>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
      Map.Entry>> entry = iterator.next();
      for (Commit value : entry.getValue().values()) {
        Scheduled timer = timers.remove(value.index());
        if (timer != null)
          timer.cancel();
        value.close();
      }
      iterator.remove();
    }
  }

}