/* * 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.copycat.server.Commit; import io.atomix.resource.ResourceStateMachine; import java.time.Duration; import java.util.*; import static io.atomix.collections.DistributedSet.Events; import static io.atomix.collections.DistributedSet.ValueEvent; /** * Distributed set state machine. * * @author <a href="http://github.com/kuujo">Jordan Halterman</a> */ public class SetState extends ResourceStateMachine { private final Map<Object, Value> map = new HashMap<>(); public SetState(Properties properties) { super(properties); } /** * Handles a contains commit. */ public boolean contains(Commit<SetCommands.Contains> commit) { try { return map.containsKey(commit.operation().value()); } finally { commit.close(); } } /** * Handles an add commit. */ public boolean add(Commit<SetCommands.Add> commit) { try { Value value = map.get(commit.operation().value()); if (value == null) { Scheduled timer = commit.operation().ttl() > 0 ? executor.schedule(Duration.ofMillis(commit.operation().ttl()), () -> { map.remove(commit.operation().value()).commit.close(); }) : null; map.put(commit.operation().value(), new Value(commit, timer)); notify(new ValueEvent<>(Events.ADD, commit.operation().value())); } else { commit.close(); } } catch (Exception e) { commit.close(); throw e; } return false; } /** * Handles a remove commit. */ public boolean remove(Commit<SetCommands.Remove> commit) { try { Value value = map.remove(commit.operation().value()); if (value != null) { try { if (value.timer != null) { value.timer.cancel(); } notify(new ValueEvent<>(Events.REMOVE, commit.operation().value())); return true; } finally { value.commit.close(); } } return false; } finally { commit.close(); } } /** * Handles a count commit. */ public int size(Commit<SetCommands.Size> commit) { try { return map.size(); } finally { commit.close(); } } /** * Handles an is empty commit. */ public boolean isEmpty(Commit<SetCommands.IsEmpty> commit) { try { return map.isEmpty(); } finally { commit.close(); } } /** * Handles an iterator commit. */ public Set<Object> iterator(Commit<SetCommands.Iterator<Object>> commit) { try { return new HashSet<>(map.keySet()); } finally { commit.close(); } } /** * Handles a clear commit. */ public void clear(Commit<SetCommands.Clear> commit) { try { delete(); } finally { commit.close(); } } @Override public void delete() { Iterator<Map.Entry<Object, Value>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Object, Value> entry = iterator.next(); Value value = entry.getValue(); if (value.timer != null) value.timer.cancel(); value.commit.close(); iterator.remove(); } } /** * Set value. */ private static class Value { private final Commit<? extends SetCommands.TtlCommand> commit; private final Scheduled timer; private Value(Commit<? extends SetCommands.TtlCommand> commit, Scheduled timer) { this.commit = commit; this.timer = timer; } } }