/**
* diqube: Distributed Query Base.
*
* Copyright (C) 2015 Bastian Gloeckle
*
* This file is part of diqube.
*
* diqube is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diqube.im.callback;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.diqube.consensus.AbstractConsensusStateMachine;
import org.diqube.consensus.ConsensusStateMachineImplementation;
import org.diqube.context.InjectOptional;
import org.diqube.im.thrift.v1.SCallback;
import org.diqube.thrift.base.thrift.RNodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.atomix.copycat.server.Commit;
/**
* Implementation of {@link IdentityCallbackRegistryStateMachine}.
*
* @author Bastian Gloeckle
*/
@ConsensusStateMachineImplementation
public class IdentityCallbackRegistryStateMachineImplementation extends AbstractConsensusStateMachine<SCallback>
implements IdentityCallbackRegistryStateMachine {
private static final Logger logger =
LoggerFactory.getLogger(IdentityCallbackRegistryStateMachineImplementation.class);
private static final String INTERNALDB_FILE_PREFIX = "identitycallback-";
private static final String INTERNALDB_DATA_TYPE = "identitycallbacks_v1";
private Map<RNodeAddress, Long> registered = new ConcurrentHashMap<>();
private Map<RNodeAddress, Commit<?>> lastCommit = new ConcurrentHashMap<>();
@InjectOptional
private List<IdentityCallbackRegistryListener> listeners;
public IdentityCallbackRegistryStateMachineImplementation() {
super(INTERNALDB_FILE_PREFIX, INTERNALDB_DATA_TYPE, () -> new SCallback());
}
@Override
protected void doInitialize(List<SCallback> entriesLoadedFromInternalDb) {
if (entriesLoadedFromInternalDb != null)
for (SCallback callback : entriesLoadedFromInternalDb) {
this.registered.put(callback.getCallbackAddr(), callback.getRegisteredAt());
}
}
private void writeCurrentCallbacksToInternalDb(long consensusIndex) {
List<SCallback> callbacks = new ArrayList<>();
for (Entry<RNodeAddress, Long> e : registered.entrySet())
callbacks.add(new SCallback(e.getKey(), e.getValue()));
super.writeCurrentStateToInternalDb(consensusIndex, callbacks);
}
@Override
public void register(Commit<Register> commit) {
Commit<?> prev = lastCommit.put(commit.operation().getCallbackNode(), commit);
RNodeAddress callbackNode = commit.operation().getCallbackNode();
long registerTime = commit.operation().getRegisterTimeMs();
registered.put(callbackNode, registerTime);
writeCurrentCallbacksToInternalDb(commit.index());
if (prev != null)
prev.close();
if (listeners != null)
listeners.forEach(l -> l.registered(callbackNode, registerTime));
}
@Override
public void unregister(Commit<Unregister> commit) {
Commit<?> lastWriteCommit = lastCommit.remove(commit.operation().getCallbackNode());
RNodeAddress callbackNode = commit.operation().getCallbackNode();
Long lastRegisterTime = registered.remove(callbackNode); // null in case unregister was called for already
// unregistered node!
writeCurrentCallbacksToInternalDb(commit.index());
if (lastWriteCommit != null)
lastWriteCommit.close();
commit.close();
if (lastRegisterTime != null && listeners != null)
listeners.forEach(l -> l.unregistered(callbackNode, lastRegisterTime));
}
@Override
public List<RNodeAddress> getAllRegistered(Commit<GetAllRegistered> commit) {
commit.close();
List<RNodeAddress> res = new ArrayList<>();
res.addAll(registered.keySet());
return res;
}
/**
* @return The list of callbacks this node currently knows of. This list might be incomplete or contain elements that
* were already unregistered!
*/
public Set<RNodeAddress> getRegisteredNodesInsecure() {
return new HashSet<>(registered.keySet());
}
/* package */ Long getCurrentRegisterTime(RNodeAddress callbackNode) {
return registered.get(callbackNode);
}
/* package */ static interface IdentityCallbackRegistryListener {
public void registered(RNodeAddress callbackNode, long registerTime);
public void unregistered(RNodeAddress callbackNode, long lastRegisterTime);
}
}