/*
* Copyright 2009 Amit Levy, Jeff Prouty, Rylan Hawkins
*
* 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 edu.washington.cs.cse490h.donut.service;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.thrift.TException;
import com.google.inject.Inject;
import edu.washington.cs.cse490h.donut.Constants;
import edu.washington.cs.cse490h.donut.business.DataPair;
import edu.washington.cs.cse490h.donut.business.EntryKey;
import edu.washington.cs.cse490h.donut.business.KeyId;
import edu.washington.cs.cse490h.donut.business.Node;
import edu.washington.cs.cse490h.donut.business.TNode;
import edu.washington.cs.cse490h.donut.service.KeyLocator.Iface;
import edu.washington.cs.cse490h.donut.service.application.DonutHashTableService;
import edu.washington.cs.cse490h.donut.util.KeyIdUtil;
/**
* @author alevy
*/
public class NodeLocator implements Iface {
private static Logger LOGGER;
private final Node node;
private final LocatorClientFactory clientFactory;
private final DonutHashTableService service;
static {
LOGGER = Logger.getLogger(NodeLocator.class.getName());
LOGGER.setLevel(Level.WARNING);
}
@Inject
public NodeLocator(Node node, DonutHashTableService service, LocatorClientFactory clientFactory) {
this.node = node;
this.service = service;
this.clientFactory = clientFactory;
}
public TNode findSuccessor(KeyId entryId) throws TException {
LOGGER.info("Request for entity [" + printNode(this.node.getTNode()) + "]: Id - \""
+ entryId.toString() + "\"");
TNode next = node.closestPrecedingNode(entryId);
if (next.equals(node.getTNode())) {
LOGGER.info("I am predecessor [" + printNode(this.node.getTNode()) + "]: Id \""
+ entryId.toString() + "\"");
return node.getSuccessor();
}
try {
LOGGER.info("I am NOT the predecessor [" + printNode(this.node.getTNode()) + "]: Id \""
+ entryId.toString() + "\" \n" + "Connecting to " + next.getPort());
TNode successor = clientFactory.get(next).findSuccessor(entryId);
clientFactory.release(next);
return successor;
} catch (RetryFailedException e) {
throw new TException(e);
}
}
public byte[] get(EntryKey key) throws TException, DataNotFoundException {
LOGGER.info("Get entity with id \"" + key.toString() + "\".");
DataPair data = service.get(key);
if (data == null) {
throw new DataNotFoundException();
}
return data.getData();
}
public void put(EntryKey key, byte[] data) throws TException, NotResponsibleForId {
if (!KeyIdUtil.isAfterXButBeforeEqualY(key.getId(), node.getPredecessor().getNodeId(), node
.getNodeId())) {
LOGGER.info("Not responsible for entity with id \"" + key.toString() + "\".");
throw new NotResponsibleForId(key.getId());
}
LOGGER.info("Put \"" + data + "\" into entity with id \"" + key.toString() + "\".");
service.put(key, data, Constants.NUM_REPLICAS);
TNode successor = node.getSuccessor();
if (!successor.equals(node.getTNode())) {
try {
clientFactory.get(successor).replicatePut(key, data,
Constants.NUM_REPLICAS - 1);
clientFactory.release(successor);
} catch (RetryFailedException e) {
throw new TException(e);
}
}
}
public void remove(EntryKey key) throws TException, NotResponsibleForId {
if (!KeyIdUtil.isAfterXButBeforeEqualY(key.getId(), node.getPredecessor().getNodeId(), node
.getNodeId())) {
LOGGER.info("Not responsible for entity with id \"" + key.toString() + "\".");
throw new NotResponsibleForId(key.getId());
}
LOGGER.info("Remove entity with id \"" + key.toString() + "\".");
service.remove(key);
TNode successor = node.getSuccessor();
if (!node.getSuccessor().equals(node.getTNode())) {
try {
clientFactory.get(successor)
.replicateRemove(key, Constants.NUM_REPLICAS - 1);
clientFactory.release(successor);
} catch (RetryFailedException e) {
throw new TException(e);
}
}
}
public void replicatePut(EntryKey key, byte[] data, int numReplicas) throws TException {
LOGGER.info("Put \"" + data + "\" into entity with id \"" + key.toString() + "\".");
service.put(key, data, numReplicas);
if (numReplicas > 0) {
TNode successor = node.getSuccessor();
try {
clientFactory.get(successor).replicatePut(key, data, numReplicas - 1);
clientFactory.release(successor);
} catch (RetryFailedException e) {
throw new TException(e);
}
}
}
public void replicateRemove(EntryKey key, int numReplicas) throws TException {
LOGGER.info("Remove entity with id \"" + key.toString() + "\".");
service.remove(key);
if (numReplicas > 0) {
TNode successor = node.getSuccessor();
try {
clientFactory.get(successor).replicateRemove(key, numReplicas - 1);
clientFactory.release(successor);
} catch (RetryFailedException e) {
throw new TException(e);
}
}
}
public DonutHashTableService getService() {
return service;
}
/*
* Should do nothing if connection completes. If the connection fails, then a TException is
* thrown.
*/
public void ping() throws TException {
}
public TNode getPredecessor() throws TException, NodeNotFoundException {
TNode predecessor = node.getPredecessor();
if (predecessor == null) {
throw new NodeNotFoundException();
}
return predecessor;
}
public List<TNode> notify(TNode n) throws TException {
if (node.getPredecessor() == null
|| KeyIdUtil.isAfterXButBeforeEqualY(n.getNodeId(), node.getPredecessor()
.getNodeId(), node.getNodeId())) {
if (node.getPredecessor() == null) {
TNode successor = node.getSuccessor();
// Copy data that belongs to me from my successor
try {
Iface successorClient = clientFactory.get(successor);
copyData(successorClient, successorClient.getDataRange(n.getNodeId(), node
.getNodeId()));
clientFactory.release(successor);
} catch (RetryFailedException e) {
throw new TException(e);
}
}
// Copy data that I should replicate from new predecessor
try {
Iface predecessorClient = clientFactory.get(n);
copyData(predecessorClient, predecessorClient.getDataRange(node.getNodeId(), n
.getNodeId()));
clientFactory.release(n);
} catch (RetryFailedException e) {
throw new TException(e);
}
node.setPredecessor(n);
}
return node.getSuccessorList();
}
private void copyData(Iface client, Set<EntryKey> keySet) throws TException {
try {
for (EntryKey key : keySet) {
byte[] data;
data = client.get(key);
service.put(key, data, Constants.NUM_REPLICAS);
}
} catch (DataNotFoundException e) {
// We were lied to! Die gracefully
LOGGER.severe("Data copying failed because of bad data. Exiting...");
System.exit(1);
}
}
public List<TNode> getFingers() throws TException {
return node.getFingers();
}
public String printNode(TNode n) {
if (n == null)
return "NULL";
else
return "" + n.getName();
}
public Set<EntryKey> getDataRange(KeyId start, KeyId end) throws TException {
return service.getRange(start, end);
}
}