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

net.hycube.join.routejoin.HyCubeRouteJoinManager Maven / Gradle / Ivy

Go to download

HyCube is a distributed hash table based on a hierarchical hypercube geometry, employing a novel variable metric adopting the Steinhaus transform.

There is a newer version: 1.0.5
Show newest version
package net.hycube.join.routejoin;

import java.util.HashMap;
import java.util.Queue;

import net.hycube.common.EntryPoint;
import net.hycube.configuration.GlobalConstants;
import net.hycube.core.HyCubeNodeId;
import net.hycube.core.HyCubeNodeIdFactory;
import net.hycube.core.HyCubeRoutingTable;
import net.hycube.core.InitializationException;
import net.hycube.core.NodeAccessor;
import net.hycube.core.NodePointer;
import net.hycube.core.RoutingTableEntry;
import net.hycube.core.UnrecoverableRuntimeException;
import net.hycube.environment.NodeProperties;
import net.hycube.environment.NodePropertiesConversionException;
import net.hycube.eventprocessing.Event;
import net.hycube.eventprocessing.EventCategory;
import net.hycube.eventprocessing.EventProcessException;
import net.hycube.eventprocessing.EventScheduler;
import net.hycube.eventprocessing.EventType;
import net.hycube.eventprocessing.ProcessEventProxy;
import net.hycube.join.JoinCallback;
import net.hycube.join.JoinCallbackEvent;
import net.hycube.join.JoinManager;
import net.hycube.logging.LogHelper;
import net.hycube.maintenance.HyCubeRecoveryExtension;
import net.hycube.maintenance.HyCubeRecoveryManager;
import net.hycube.messaging.messages.HyCubeMessage;
import net.hycube.messaging.messages.HyCubeMessageFactory;
import net.hycube.messaging.messages.HyCubeMessageType;
import net.hycube.messaging.messages.Message;
import net.hycube.messaging.processing.MessageSendProcessInfo;
import net.hycube.messaging.processing.ProcessMessageException;
import net.hycube.nexthopselection.HyCubeNextHopSelectionParameters;
import net.hycube.nexthopselection.NextHopSelector;
import net.hycube.transport.NetworkAdapterException;
import net.hycube.transport.NetworkNodePointer;
import net.hycube.utils.HashMapUtils;
import net.hycube.utils.ObjectToStringConverter.MappedType;

/**
 * Join manager class.
 * @author Artur Olszak
 *
 */
public class HyCubeRouteJoinManager implements JoinManager {

	private static org.apache.commons.logging.Log devLog = LogHelper.getDevLog(HyCubeRouteJoinManager.class);
	private static org.apache.commons.logging.Log msgLog = LogHelper.getMessagesLog();
	
	protected static final String PROP_KEY_NEXT_HOP_SELECTOR_KEY = "NextHopSelectorKey";
	protected static final String PROP_KEY_PREFIX_MISMATCH_HEURISTIC_DISABLED_FOR_JOIN_MESSAGES = "PMHDisabledForJoinMessages";
	protected static final String PROP_KEY_USE_STEINHAUS_TRANSFORM = "UseSteinhausTransform";
	protected static final String PROP_KEY_JOIN_CALLBACK_EVENT_KEY = "JoinCallbackEventKey";
	protected static final String PROP_KEY_JOIN_TIMEOUT_EVENT_KEY = "JoinTimeoutEventKey";
	protected static final String PROP_KEY_WAIT_AFTER_FINAL_JOIN_REPLY_TIMEOUT_EVENT_KEY = "WaitAfterFinalJoinReplyTimeoutEventKey";
	protected static final String PROP_KEY_JOIN_TIMEOUT = "JoinTimeout";
	protected static final String PROP_KEY_WAIT_TIME_AFTER_FINAL_JOIN_REPLY = "WaitTimeAfterFinalJoinReply";
	protected static final String PROP_KEY_INCLUDE_NS_IN_JOIN_REPLY = "IncludeNSInJoinReply";
	protected static final String PROP_KEY_INCLUDE_RT_IN_JOIN_REPLY = "IncludeRTInJoinReply";
	protected static final String PROP_KEY_INCLUDE_SELF_IN_JOIN_REPLY = "IncludeSelfInJoinReply";
	protected static final String PROP_KEY_INCLUDE_NS_IN_JOIN_REPLY_FINAL = "IncludeNSInJoinReplyFinal";
	protected static final String PROP_KEY_INCLUDE_RT_IN_JOIN_REPLY_FINAL = "IncludeRTInJoinReplyFinal";
	protected static final String PROP_KEY_INCLUDE_SELF_IN_JOIN_REPLY_FINAL = "IncludeSelfInJoinReplyFinal";
	
	protected static final String PROP_KEY_RECOVERY_NS_AFTER_JOIN = "RecoveryNSAfterJoin";
	protected static final String PROP_KEY_RECOVERY_AFTER_JOIN = "RecoveryAfterJoin";
	protected static final String PROP_KEY_RECOVERY_EXTENSION_KEY = "RecoveryExtensionKey";
	
	protected static final String PROP_KEY_DISCOVER_PUBLIC_NETWORK_ADDRESS = "DiscoverPublicNetworkAddress";
	
	
	protected NodeAccessor nodeAccessor;
	protected NodeProperties properties;
	
	protected HyCubeNodeId nodeId;
	
	protected HyCubeRoutingTable routingTable;
	
	protected String joinCallbackEventTypeKey;
	protected String joinTimeoutEventKey;
	protected String waitAfterFinalJoinReplyTimeoutEventTypeKey;
	
	protected String nextHopSelectorKey;
	protected boolean pmhDisabledForJoinMessages;
	protected boolean useSteinhausTransform;
	protected int joinTimeout;
	protected int waitTimeAfterFinalJoinReply;
	protected boolean includeNSInJoinReply;
	protected boolean includeRTInJoinReply;
	protected boolean includeSelfInJoinReply;
	protected boolean includeNSInJoinReplyFinal;
	protected boolean includeRTInJoinReplyFinal;
	protected boolean includeSelfInJoinReplyFinal;
	
	protected boolean discoverPublicNetworkAddress;
	
	
	protected EventType joinCallbackEventType;
	protected EventType joinTimeoutEventType;
	protected EventType waitAfterFinalJoinReplyTimeoutEventType;
	
	HyCubeRouteJoinTimeoutEventProxy joinTimeoutEventProxy;
	HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEventProxy waitAfterFinalJoinReplyTimeoutEventProxy;
	
	protected NextHopSelector nextHopSelector;
	
	protected boolean recoveryNSAfterJoin;
	protected boolean recoveryAfterJoin;
	protected HyCubeRecoveryManager recoveryManager;
	
	protected HyCubeMessageFactory messageFactory;
	protected HyCubeNodeIdFactory nodeIdFactory;
	
	
	protected Object joinManagerLock = new Object();	//used top synchronize operations on the manager's data (like joines list)

	protected int nextJoinId;
	
	protected HashMap joinsData;
	
	
	@Override
	public void initialize(NodeAccessor nodeAccessor, NodeProperties properties) throws InitializationException {
		
		nextJoinId = Integer.MIN_VALUE;
		
		this.nodeAccessor = nodeAccessor;
		this.properties = properties;
	
		joinCallbackEventTypeKey = properties.getProperty(PROP_KEY_JOIN_CALLBACK_EVENT_KEY);
		joinCallbackEventType = new EventType(EventCategory.extEvent, joinCallbackEventTypeKey);
		
		joinTimeoutEventKey = properties.getProperty(PROP_KEY_JOIN_TIMEOUT_EVENT_KEY);
		joinTimeoutEventType = new EventType(EventCategory.extEvent, joinTimeoutEventKey);
		
		waitAfterFinalJoinReplyTimeoutEventTypeKey = properties.getProperty(PROP_KEY_WAIT_AFTER_FINAL_JOIN_REPLY_TIMEOUT_EVENT_KEY);
		waitAfterFinalJoinReplyTimeoutEventType = new EventType(EventCategory.extEvent, waitAfterFinalJoinReplyTimeoutEventTypeKey);
		
		
		nextHopSelectorKey = properties.getProperty(PROP_KEY_NEXT_HOP_SELECTOR_KEY);
		nextHopSelector = nodeAccessor.getNextHopSelector(nextHopSelectorKey);
		
		
		
		
		if (! (nodeAccessor.getNodeId() instanceof HyCubeNodeId)) throw new InitializationException(InitializationException.Error.NODE_INITIALIZATION_ERROR, "The node id is expected to be an instance of:" + HyCubeNodeId.class.getName());
		this.nodeId = (HyCubeNodeId) nodeAccessor.getNodeId();
		
		if (! (nodeAccessor.getRoutingTable() instanceof HyCubeRoutingTable)) throw new InitializationException(InitializationException.Error.NODE_INITIALIZATION_ERROR, "The routing table is expected to be an instance of:" + HyCubeRoutingTable.class.getName());
		routingTable = (HyCubeRoutingTable) nodeAccessor.getRoutingTable();
		
		
		String recoveryExtensionKey = properties.getProperty(PROP_KEY_RECOVERY_EXTENSION_KEY);
		if (recoveryExtensionKey == null || recoveryExtensionKey.trim().isEmpty()) throw new InitializationException(InitializationException.Error.INVALID_PARAMETER_VALUE, properties.getAbsoluteKey(PROP_KEY_RECOVERY_EXTENSION_KEY), "Invalid parameter value: " + properties.getAbsoluteKey(PROP_KEY_RECOVERY_EXTENSION_KEY));
		if (!(nodeAccessor.getExtension(recoveryExtensionKey) instanceof HyCubeRecoveryExtension)) throw new InitializationException(InitializationException.Error.MESSAGE_RECEIVER_INITIALIZATION_ERROR, null, "The recovery extension is expected to be an instance of " + HyCubeRecoveryExtension.class.getName());
		HyCubeRecoveryExtension recoveryExtension = (HyCubeRecoveryExtension) nodeAccessor.getExtension(recoveryExtensionKey);
		
		
		
		try {

			pmhDisabledForJoinMessages = (Boolean) properties.getProperty(PROP_KEY_PREFIX_MISMATCH_HEURISTIC_DISABLED_FOR_JOIN_MESSAGES, MappedType.BOOLEAN);
			
			try {
				if (properties.containsKey(PROP_KEY_USE_STEINHAUS_TRANSFORM)) {
					this.useSteinhausTransform = (Boolean) properties.getProperty(PROP_KEY_USE_STEINHAUS_TRANSFORM, MappedType.BOOLEAN);
				}
				else {
					this.useSteinhausTransform = false;
				}
			} catch (NodePropertiesConversionException e) {
				throw new InitializationException(InitializationException.Error.INVALID_PARAMETER_VALUE, e.getKey(), "An error occured while reading a node parameter. The property could not be converted: " + e.getKey(), e);
			}
			
			joinTimeout = (Integer) properties.getProperty(PROP_KEY_JOIN_TIMEOUT, MappedType.INT);
			
			waitTimeAfterFinalJoinReply = (Integer) properties.getProperty(PROP_KEY_WAIT_TIME_AFTER_FINAL_JOIN_REPLY, MappedType.INT);
			
			includeNSInJoinReply = (Boolean) properties.getProperty(PROP_KEY_INCLUDE_NS_IN_JOIN_REPLY, MappedType.BOOLEAN);
			includeRTInJoinReply = (Boolean) properties.getProperty(PROP_KEY_INCLUDE_RT_IN_JOIN_REPLY, MappedType.BOOLEAN);
			includeSelfInJoinReply = (Boolean) properties.getProperty(PROP_KEY_INCLUDE_SELF_IN_JOIN_REPLY, MappedType.BOOLEAN);
			includeNSInJoinReplyFinal = (Boolean) properties.getProperty(PROP_KEY_INCLUDE_NS_IN_JOIN_REPLY_FINAL, MappedType.BOOLEAN);
			includeRTInJoinReplyFinal = (Boolean) properties.getProperty(PROP_KEY_INCLUDE_RT_IN_JOIN_REPLY_FINAL, MappedType.BOOLEAN);
			includeSelfInJoinReplyFinal = (Boolean) properties.getProperty(PROP_KEY_INCLUDE_SELF_IN_JOIN_REPLY_FINAL, MappedType.BOOLEAN);

			discoverPublicNetworkAddress = (Boolean) properties.getProperty(PROP_KEY_DISCOVER_PUBLIC_NETWORK_ADDRESS, MappedType.BOOLEAN);
			
			recoveryNSAfterJoin = (Boolean) properties.getProperty(PROP_KEY_RECOVERY_NS_AFTER_JOIN, MappedType.BOOLEAN);
			recoveryAfterJoin = (Boolean) properties.getProperty(PROP_KEY_RECOVERY_AFTER_JOIN, MappedType.BOOLEAN);
			
			
		} catch (NodePropertiesConversionException e) {
			throw new InitializationException(InitializationException.Error.NODE_INITIALIZATION_ERROR, null, "Unable to initialize the join manager instance. Invalid parameter value: " + e.getKey() + ".", e);
		}
		
		
		if (recoveryNSAfterJoin || recoveryAfterJoin) {
			recoveryManager = recoveryExtension.getRecoveryManager();
			if (recoveryManager == null) throw new InitializationException(InitializationException.Error.MESSAGE_RECEIVER_INITIALIZATION_ERROR, null, "The recovery manager is null");
		}
		
		
		if (! (nodeAccessor.getMessageFactory() instanceof HyCubeMessageFactory)) throw new UnrecoverableRuntimeException("The message factory is expected to be an instance of: " + HyCubeMessageFactory.class.getName() + ".");
		messageFactory = (HyCubeMessageFactory) nodeAccessor.getMessageFactory();
		
		if (! (nodeAccessor.getNodeIdFactory() instanceof HyCubeNodeIdFactory)) throw new UnrecoverableRuntimeException("The node id factory is expected to be an instance of: " + HyCubeNodeIdFactory.class.getName() + ".");
		nodeIdFactory = (HyCubeNodeIdFactory) nodeAccessor.getNodeIdFactory();
		
		
		joinTimeoutEventProxy = new HyCubeRouteJoinTimeoutEventProxy();
		waitAfterFinalJoinReplyTimeoutEventProxy = new HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEventProxy();
		
		
		joinsData = new HashMap(HashMapUtils.getHashMapCapacityForElementsNum(GlobalConstants.INITIAL_JOINS_DATA_COLLECTION_SIZE, GlobalConstants.DEFAULT_HASH_MAP_LOAD_FACTOR), GlobalConstants.DEFAULT_HASH_MAP_LOAD_FACTOR);
		
		
		
	}

	
	protected int getNextJoinId() {
		synchronized (joinManagerLock) {
			int joinId = nextJoinId;
			nextJoinId++;
			return joinId;
		}
	}
	
	@Override
	public JoinCallback join(String bootstrapNodeAddress, JoinCallback joinCallback, Object callbackArg) {
		return join(bootstrapNodeAddress, joinCallback, callbackArg, null);
	}
	
	@Override
	public JoinCallback join(String bootstrapNodeAddress, JoinCallback joinCallback, Object callbackArg, Object[] joinParameters) {
		
		boolean secureRouting = false;
		boolean skipRandomNextHops = false;
		
		if (joinParameters != null) {
			
			secureRouting = getJoinParameterSecureRouting(joinParameters);
			skipRandomNextHops = getJoinParameterSkipRandomNextHops(joinParameters);

		}
		
		synchronized (joinManagerLock) {
			
			//generate the join id
			int joinId = getNextJoinId();
			
			
			//if exists, discard previous join with the same id:
			HyCubeRouteJoinData oldJoinData = null;
			synchronized (joinManagerLock) {
				if (joinsData.containsKey(joinId)) {
					oldJoinData = joinsData.get(joinId);
					joinsData.remove(joinId);
				}
			}
			if (oldJoinData != null) oldJoinData.discard();
			
			
			
			//prepare the join structure
			HyCubeRouteJoinData joinData = new HyCubeRouteJoinData(joinId, bootstrapNodeAddress, secureRouting, skipRandomNextHops);
			
			joinsData.put(joinId, joinData);
			
			
			if (joinCallback != null) {
			
				//registerJoinCallback
				joinData.setJoinCallback(joinCallback);
				joinData.setCallbackArg(callbackArg);
							
			}

			if (bootstrapNodeAddress != null && bootstrapNodeAddress.length() != 0) {
				//send the join message to the bootstrapNode
				sendJoinRequest(joinId, nodeId, bootstrapNodeAddress, discoverPublicNetworkAddress, joinData.secureRouting, joinData.skipRandomNextHops);
			}
			else {
				//no join message sent - this node is the only node in the DHT
				
				//schedule the join callback event:
				enqueueCallbackEvent(joinCallback, callbackArg);
				
				//remove this join from the joins list:
				synchronized (joinManagerLock) {
					joinsData.remove(joinData.getJoinId());
				}

			}
			
			
			
			
		}
		
		
		return joinCallback;
		
	}

	
	@Override
	public EventType getJoinCallbackEventType() {
		return joinCallbackEventType;
	}

	
	public EventType getJoinTimeoutEventType() {
		return joinTimeoutEventType;
	}
	
	public EventType getWaitAfterFinalJoinReplyTimeoutEventType() {
		return waitAfterFinalJoinReplyTimeoutEventType;
	}

	
	
	protected void sendJoinRequest(int joinId, HyCubeNodeId joinNodeId, String bootstrapNodeAddress, boolean discoverPublicNetworkAddress, boolean secureRouting, boolean skipRandomNextHops) {

		//prepare the message:
		
		int messageSerialNo = nodeAccessor.getNextMessageSerialNo();
		byte[] joinMessageData = (new HyCubeRouteJoinMessageData(joinId, joinNodeId, discoverPublicNetworkAddress)).getBytes(nodeIdFactory.getDimensions(), nodeIdFactory.getDigitsCount());
		HyCubeMessage joinMessage = messageFactory.newMessage(messageSerialNo, nodeAccessor.getNodeId(), joinNodeId, nodeAccessor.getNetworkAdapter().getPublicAddressBytes(), HyCubeMessageType.JOIN, nodeAccessor.getNodeParameterSet().getMessageTTL(), (short)0, false, false, (short)0, (short)0, joinMessageData);
		
		//this message is being routed (the join manager is also a routing manager):
		joinMessage.setTtl((short) (joinMessage.getTtl() - 1));
		joinMessage.setHopCount((short) (joinMessage.getHopCount() + 1));


		//set the default routing paremeters:
		joinMessage.setPMHApplied(false);
		joinMessage.setSteinhausTransformApplied(this.useSteinhausTransform);
		joinMessage.setSteinhausPoint(joinMessage.getSenderId());
		
		joinMessage.setSecureRoutingApplied(secureRouting);
		joinMessage.setSkipRandomNumOfNodesApplied(skipRandomNextHops);
		
		
		//send the message directly to the recipient:
		try {
			nodeAccessor.sendMessage(new MessageSendProcessInfo(joinMessage, nodeAccessor.getNetworkAdapter().createNetworkNodePointer(bootstrapNodeAddress), false), GlobalConstants.WAIT_ON_BKG_MSG_SEND);
		} catch (NetworkAdapterException e) {
			throw new UnrecoverableRuntimeException("An exception has been thrown while trying to send a join request to a node.", e);
		} catch (ProcessMessageException e) {
			throw new UnrecoverableRuntimeException("An exception has been thrown while trying to send a join request to a node.", e);
		}
		
		
		//schedule the request time out event
		scheduleJoinTimeout(joinId);
		
		
	}
	
	
	protected void sendJoinReply(int joinId, NodePointer recipient, NetworkNodePointer requestor, NodePointer[] nodesReturned, boolean finalJoinReply) {
		
		//prepare the message:
		
		int messageSerialNo = nodeAccessor.getNextMessageSerialNo();
		byte[] joinReplyMessageData = (new HyCubeRouteJoinReplyMessageData(joinId, requestor, nodesReturned, finalJoinReply)).getBytes(nodeIdFactory.getDimensions(), nodeIdFactory.getDigitsCount());
		Message joinMessage = messageFactory.newMessage(messageSerialNo, nodeAccessor.getNodeId(), recipient.getNodeId(), nodeAccessor.getNetworkAdapter().getPublicAddressBytes(), HyCubeMessageType.JOIN_REPLY, nodeAccessor.getNodeParameterSet().getMessageTTL(), (short)0, false, false, (short)0, (short)0, joinReplyMessageData); 

		
		//send the message directly to the recipient:
		try {
			nodeAccessor.sendMessage(new MessageSendProcessInfo(joinMessage, recipient.getNetworkNodePointer(), false), GlobalConstants.WAIT_ON_BKG_MSG_SEND);
		} catch (NetworkAdapterException e) {
			throw new UnrecoverableRuntimeException("An exception has been thrown while trying to send a join request to a node.", e);
		} catch (ProcessMessageException e) {
			throw new UnrecoverableRuntimeException("An exception has been thrown while trying to send a join request to a node.", e);
		}
		
		
	}
	
	
	
	protected void scheduleJoinTimeout(int joinId) {
		//create the event:
		Event event = new HyCubeRouteJoinTimeoutEvent(this, joinTimeoutEventProxy, joinId);
				
		//schedule the event:
		Queue queue = nodeAccessor.getEventQueue(joinTimeoutEventType);
		EventScheduler scheduler = nodeAccessor.getEventScheduler();
		scheduler.scheduleEventWithDelay(event, queue, joinTimeout);
		
	}

	
	public class HyCubeRouteJoinTimeoutEventProxy implements ProcessEventProxy {
		@Override
		public void processEvent(Event event) throws EventProcessException {
			if (! (event instanceof HyCubeRouteJoinTimeoutEvent)) throw new EventProcessException("Invalid event type scheduled to be processed by " + getClass().getName() + ". The expected event class: " + HyCubeRouteJoinTimeoutEvent.class.getName() + ".");
			HyCubeRouteJoinTimeoutEvent joinEvent = (HyCubeRouteJoinTimeoutEvent)event;
			try {
				joinTimedOut(joinEvent.joinId);
			}
			catch (Exception e) {
				throw new EventProcessException("An exception was thrown while processing a join request timeout event.", e);
			}
		}
	}
	
	
	protected void scheduleWaitAfterFinalReplyTimeout(int joinId) {
		
		//create the event:
		Event event = new HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEvent(this, waitAfterFinalJoinReplyTimeoutEventProxy, joinId);
						
		//schedule the event:
		Queue queue = nodeAccessor.getEventQueue(waitAfterFinalJoinReplyTimeoutEventType);
		EventScheduler scheduler = nodeAccessor.getEventScheduler();
		scheduler.scheduleEventWithDelay(event, queue, waitTimeAfterFinalJoinReply);
		
	}
	
	
	public class HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEventProxy implements ProcessEventProxy {
		@Override
		public void processEvent(Event event) throws EventProcessException {
			if (! (event instanceof HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEvent)) throw new EventProcessException("Invalid event type scheduled to be processed by " + getClass().getName() + ". The expected event class: " + HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEvent.class.getName() + ".");
			HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEvent joinEvent = (HyCubeRouteJoinWaitAfterFinalJoinReplyTimeoutEvent)event;
			waitAfterFinalReplyTimedOut(joinEvent.joinId);
		}
	}
	
	
	
	protected void joinTimedOut(int joinId) {

		synchronized (joinManagerLock) {
			if (joinsData.containsKey(joinId)) {
				
				HyCubeRouteJoinData joinData = joinsData.get(joinId);
				joinsData.remove(joinId);
				
				if (recoveryNSAfterJoin) {
					//scheduleRecoveryNS
					recoveryManager.recoverNS();
				}
				
				if (recoveryAfterJoin) {
					//scheduleRecovery
					recoveryManager.recover();
				}
				
				
				//schedule the join callback
				enqueueCallbackEvent(joinData.joinCallback, joinData.callbackArg);
				
				
			}
			//else the join procedure was already finished - do nothing
		}
		
	}
	
	
	protected void waitAfterFinalReplyTimedOut(int joinId) {
		
		synchronized (joinManagerLock) {
			if (joinsData.containsKey(joinId)) {
				HyCubeRouteJoinData joinData = joinsData.get(joinId);
				joinsData.remove(joinId);
				
				if (recoveryNSAfterJoin) {
					//scheduleRecoveryNS
					recoveryManager.recoverNS();
				}
				
				if (recoveryAfterJoin) {
					//scheduleRecovery
					recoveryManager.recover();
				}
				
				
				//schedule the join callback
				enqueueCallbackEvent(joinData.joinCallback, joinData.callbackArg);
				
				
				
			}
			//else the join procedure was already finished
		}
		
	}
	
	
	protected void enqueueCallbackEvent(JoinCallback joinCallback, Object callbackArg) {
		
		if (joinCallback == null) return;
		
		//create the event
		Event event = new JoinCallbackEvent(this, joinCallback, callbackArg);
		
		//insert to the appropriate event queue
		try {
			nodeAccessor.getEventQueue(joinCallbackEventType).put(event);
		} catch (InterruptedException e) {
			//this should never happen
			throw new UnrecoverableRuntimeException("An exception was thrown while inserting an event to an event queue.");
		}

	}
	

	public void processJoinRequest(NodePointer sender, int joinId, HyCubeNodeId joinNodeId, HyCubeMessage joinMsg, NetworkNodePointer directSender, boolean discoverPublicNetworkAddress) {
		
		boolean finalJoinReply = false;

		
		HyCubeNextHopSelectionParameters parameters = new HyCubeNextHopSelectionParameters();
		parameters.setPMHApplied(joinMsg.isPMHApplied());
		parameters.setSteinhausTransformApplied(joinMsg.isSteinhausTransformApplied());
		parameters.setSkipRandomNumOfNodesApplied(joinMsg.isSkipRandomNumOfNodesApplied());
		parameters.setSecureRoutingApplied(joinMsg.isSecureRoutingApplied());
		parameters.setSteinhausPoint(joinMsg.getSteinhausPointId());
		parameters.setIncludeMoreDistantNodes(false);
		parameters.setSkipTargetNode(true);
		
		if (pmhDisabledForJoinMessages) parameters.setPreventPMH(true);
		else parameters.setPreventPMH(false);

		//find next hop for the join message
		NodePointer nextHop = nextHopSelector.findNextHop(joinMsg.getRecipientId(), parameters);
		
		if (nextHop == null || joinMsg.getTtl() == 0) finalJoinReply = true;
		
		NodePointer[] nodesReturned = null;
		
		
		
		
		int nodesReturnedNum = 0;

		if (includeSelfInJoinReply || (finalJoinReply && includeSelfInJoinReplyFinal)) {
			nodesReturnedNum++;
		}
		
		if (includeNSInJoinReply || (finalJoinReply && includeNSInJoinReplyFinal)) {
			routingTable.getNsLock().readLock().lock();
			nodesReturnedNum += routingTable.getNsMap().size();
		}
		
		if (includeRTInJoinReply || (finalJoinReply && includeRTInJoinReplyFinal)) {
			routingTable.getRt1Lock().readLock().lock();
			routingTable.getRt2Lock().readLock().lock();
			nodesReturnedNum += routingTable.getRt1Map().size() + routingTable.getRt2Map().size();
		}
		
		HashMap nodesReturnedMap = new HashMap(HashMapUtils.getHashMapCapacityForElementsNum(nodesReturnedNum, GlobalConstants.DEFAULT_HASH_MAP_LOAD_FACTOR), GlobalConstants.DEFAULT_HASH_MAP_LOAD_FACTOR);
		
		if (includeSelfInJoinReply || (finalJoinReply && includeSelfInJoinReplyFinal)) {
			nodesReturnedMap.put(nodeAccessor.getNodePointer().getNodeIdHash(), nodeAccessor.getNodePointer());
		}
		
		if (includeNSInJoinReply || (finalJoinReply && includeNSInJoinReplyFinal)) {
			for (RoutingTableEntry rte : routingTable.getNsMap().values()) {
				nodesReturnedMap.put(rte.getNodeIdHash(), rte.getNode());
			}
			routingTable.getNsLock().readLock().unlock();
		}
		
		if (includeRTInJoinReply || (finalJoinReply && includeRTInJoinReplyFinal)) {
			for (RoutingTableEntry rte : routingTable.getRt1Map().values()) {
				nodesReturnedMap.put(rte.getNodeIdHash(), rte.getNode());
			}
			routingTable.getRt1Lock().readLock().unlock();
			
			for (RoutingTableEntry rte : routingTable.getRt2Map().values()) {
				nodesReturnedMap.put(rte.getNodeIdHash(), rte.getNode());
			}
			routingTable.getRt2Lock().readLock().unlock();
		}
			

			
		nodesReturned = nodesReturnedMap.values().toArray(new NodePointer[0]);
		
		
		
		
		NodePointer joinNode;
		NetworkNodePointer joinNodePublicAddress;
		if (discoverPublicNetworkAddress) {
			//return the direct sender node address only if the request was sent directly from the initiating node:
			if (joinMsg.getHopCount() == 1) {
				joinNodePublicAddress = directSender;
				
				joinNode = new NodePointer();
				joinNode.setNodeId(sender.getNodeId());
				joinNode.setNetworkNodePointer(directSender);
				
			}
			else {
				joinNodePublicAddress = null;
				
				joinNode = sender;
				
			}
		}
		else {
			joinNodePublicAddress = null;
			
			joinNode = sender;
			
		}
		
		
		
		
		//send the reply to the requesting node:
		
		sendJoinReply(joinId, joinNode, joinNodePublicAddress, nodesReturned, finalJoinReply);
		
		
		//if next hop was found, route the message:
		if (nextHop != null && joinMsg.getTtl() > 0) {
			try {
				
				if (devLog.isDebugEnabled()) {
					devLog.debug("Routing message #" + joinMsg.getSerialNoAndSenderString() + ".");
				}
				if (msgLog.isInfoEnabled()) {
					msgLog.info("Routing message #" + joinMsg.getSerialNoAndSenderString() + ".");
				}
				
				if (joinMsg.getTtl() == 0) {
					if (devLog.isDebugEnabled()) {
						devLog.debug("Message #" + joinMsg.getSerialNoAndSenderString() + " dropped. TTL = 0.");
					}
					if (msgLog.isInfoEnabled()) {
						msgLog.info("Message #" + joinMsg.getSerialNoAndSenderString() + " dropped. TTL = 0.");
					}
				}
				
				//update the join node - to the one with its real network address
				joinMsg.setSenderNetworkAddress(joinNode.getNetworkNodePointer().getAddressBytes());
				
				
				joinMsg.setPMHApplied(parameters.isPMHApplied());
				joinMsg.setSteinhausTransformApplied(parameters.isSteinhausTransformApplied());
				joinMsg.setSkipRandomNumOfNodesApplied(parameters.isSkipRandomNumOfNodesApplied());
				joinMsg.setSecureRoutingApplied(parameters.isSecureRoutingApplied());
				joinMsg.setSteinhausPoint(parameters.getSteinhausPoint());
				joinMsg.setTtl((short) (joinMsg.getTtl() - 1));
				joinMsg.setHopCount((short) (joinMsg.getHopCount() + 1));

								
				MessageSendProcessInfo info = new MessageSendProcessInfo(joinMsg, nextHop.getNetworkNodePointer());
				
				if (devLog.isDebugEnabled()) {
					devLog.debug("Sending message #" + joinMsg.getSerialNoAndSenderString() + " to " + info.getDirectRecipient().getAddressString() + ".");
				}
				if (msgLog.isInfoEnabled()) {
					msgLog.debug("Sending message #" + joinMsg.getSerialNoAndSenderString() + " to " + info.getDirectRecipient().getAddressString() + ".");
				}
				
				nodeAccessor.sendMessageToNode(info, GlobalConstants.WAIT_ON_BKG_MSG_SEND);
				
			} catch (NetworkAdapterException e) {
				throw new UnrecoverableRuntimeException("An exception has been thrown while trying to route a join message.", e);
			} catch (ProcessMessageException e) {
				throw new UnrecoverableRuntimeException("An exception has been thrown while trying to route a join message.", e);
			}
		}
		
		
	}

	
	public void processJoinResponse(NodePointer sender, int joinId,	NetworkNodePointer publicNetworkNodePoiner, NodePointer[] nodesFound, boolean finalJoinReply) {
		
		HyCubeRouteJoinData joinData;
		synchronized(joinManagerLock) {
			if (joinsData.containsKey(joinId)) {
				joinData = joinsData.get(joinId);
			}
			else return;	//the join has already been terminated, or the join id is wrong
		}
		
		
		synchronized (joinData) {
			if (discoverPublicNetworkAddress) {
				if (! joinData.publicNetworkAddressDiscovered) {
					if (publicNetworkNodePoiner != null) {
						nodeAccessor.getNetworkAdapter().setPublicAddress(publicNetworkNodePoiner);
						joinData.publicNetworkAddressDiscovered = true;
					}
				}
			}	
		}
		
		
		//process the nodes found:
		for (NodePointer np : nodesFound) {
			nodeAccessor.getNotifyProcessor().processNotify(np, nodeAccessor.getEnvironment().getTimeProvider().getCurrentTime());
		}
		
		if (finalJoinReply) {
			//schedule the wait after final join:
			scheduleWaitAfterFinalReplyTimeout(joinId);
			
		}

		
		
	}


	@Override
	public EntryPoint getEntryPoint() {
		return null;
	}
	
	
	
	@Override
	public void discard() {	
	}
	
	
	
	public static Object[] createJoinParameters(Boolean secureRouting, Boolean skipRandomNextHops) {
		Object[] parameters = new Object[] {secureRouting, skipRandomNextHops};
		return parameters;
	}
	
	public static boolean getJoinParameterSecureRouting(Object[] parameters) {
		if (parameters == null) return false;
		if (! (parameters.length > 0) || (!(parameters[0] instanceof Boolean))) return false;
		return (Boolean)parameters[0];
	}
	
	public static boolean getJoinParameterSkipRandomNextHops(Object[] parameters) {
		if (parameters == null) return false;
		if (! (parameters.length > 1) || (!(parameters[1] instanceof Boolean))) return false;
		return (Boolean)parameters[1];
	}
	
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy