{
assert (id != null);
// Choose the node. This needs to occur outside of a transaction,
// as it could take a while.
final Node oldNode = old;
final long newNodeId;
try {
newNodeId = assignPolicy.chooseNode(id, requestingNode);
} catch (NoNodesAvailableException ex) {
logger.logThrow(Level.FINEST, ex, "mapToNewNode: id {0} from {1}" +
" failed because no live nodes are available",
id, oldNode);
throw ex;
}
if (oldNode != null && newNodeId == oldNode.getId()) {
// We picked the same node. This might be OK - the system might
// only have one node, or the current node might simply be the
// best one available.
//
// TBD - we might want a method on chooseNode which explicitly
// excludes the current node, and returns something (a negative
// number?) if there is no other choice.
return newNodeId;
}
// Calculate the lookup keys for both the old and new nodes.
// The id key is the same for both old and new.
final String idkey = NodeMapUtil.getIdentityKey(id);
// The oldNode will be null if this is the first assignment.
final String oldNodeKey = (oldNode == null) ? null :
NodeMapUtil.getNodeKey(oldNode.getId(), id);
final String oldStatusKey = (oldNode == null) ? null :
NodeMapUtil.getPartialStatusKey(id, oldNode.getId());
final String newNodekey = NodeMapUtil.getNodeKey(newNodeId, id);
final String newStatuskey = (serviceName == null) ? null :
NodeMapUtil.getStatusKey(id, newNodeId, serviceName);
final IdentityMO newidmo = new IdentityMO(id, newNodeId);
try {
runTransactionally(new AbstractKernelRunnable("MoveIdentity") {
public void run() {
// First, we clean up any old mappings.
if (oldNode != null) {
try {
// Find the old IdentityMO, with the old node info.
IdentityMO oldidmo = (IdentityMO)
dataService.getServiceBinding(idkey);
// Check once more for the assigned node - someone
// else could have mapped it before we got here.
// If so, just return.
if (oldidmo.getNodeId() != oldNode.getId()) {
return;
}
//Remove the old node->id key.
dataService.removeServiceBinding(oldNodeKey);
// Remove the old status information. We don't
// retain any info about the old node's status.
Iterator<String> iter =
BoundNamesUtil.getServiceBoundNamesIterator(
dataService, oldStatusKey);
while (iter.hasNext()) {
iter.next();
iter.remove();
}
// Remove the old IdentityMO with the old node info.
dataService.removeObject(oldidmo);
} catch (NameNotBoundException e) {
// The identity was removed before we could
// reassign it to a new node.
// Simply make the new assignment, as if oldNode
// was null to begin with.
}
}
// Add (or update) the id->node mapping.
dataService.setServiceBinding(idkey, newidmo);
// Add the node->id mapping
dataService.setServiceBinding(newNodekey, newidmo);
// Reference count
if (newStatuskey != null) {
dataService.setServiceBinding(newStatuskey, newidmo);
} else {
// This server has started the move, either through
// a node failure or load balancing. Add the identity
// to the remove list so we will notice if the client
// never logs back in.
try {
canRemove(newidmo.getIdentity());
} catch (IOException ex) {
// won't happen; this is a local call
}
}
} });
GetNodeTask atask = new GetNodeTask(newNodeId);
runTransactionally(atask);
// Tell our listeners
notifyListeners(oldNode, atask.getNode(), id);
} catch (Exception e) {
// We can get an IllegalStateException if this server shuts
// down while we're moving identities from failed nodes.
// TODO - check that those identities are properly removed.
// Hmmm. we've probably left some garbage in the data store.
// The most likely problem is one in our own code.
logger.logThrow(Level.FINE, e,
"Move {0} mappings from {1} to {2} failed",
id, oldNode.getId(), newNodeId);
}
return newNodeId;
}