com.crankuptheamps.client.MessageRouter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of amps-client Show documentation
Show all versions of amps-client Show documentation
AMPS Java client by 60East Technologies, Inc.
////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2020 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties. This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights. This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
package com.crankuptheamps.client;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* MessageRouter is used to register and manage a list of handler objects for messages,
* as well as routing messages to those handlers as messages arrive.
* MessageRouter also "knows" about the meaning of AMPS acks and can use them to automatically
* clean up routes as acks arrive.
*/
public class MessageRouter
{
/**
* Adds a route to self.
* @param commandId_ The command, query, or subid used for this route.
* @param messageHandler_ The message handler to route to
* @param requestedAcks_ The acks requested by the user for this command
* @param systemAcks_ The acks not requested by the end user, but requested
* by AMPS. These will not be delivered to the message
* handler, but are still processed for auto-removal.
* @param isSubscribe_ True if this route is for an ongoing subscription
*/
public void addRoute(CommandId commandId_, MessageHandler messageHandler_, int requestedAcks_, int systemAcks_, boolean isSubscribe_)
{
_routes.put(commandId_, new MessageRoute(messageHandler_, requestedAcks_, systemAcks_, isSubscribe_));
}
/**
* Remove a route from self.
* @param commandId_ The route to remove
* @return true if the route was removed.
*/
public boolean removeRoute(CommandId commandId_)
{
return _routes.remove(commandId_) != null;
}
/**
* Find and return a route
* @param commandId_ The command id for this route
* @return The MessageHandler registered for this route, or null if none is registered.
*/
public MessageHandler findRoute(CommandId commandId_)
{
MessageRoute route = _routes.get(commandId_);
if(route != null)
return route.getMessageHandler();
else
return null;
}
/**
* Return whether or not a command id has a route
* @param commandId_ The command id for this route
* @return If a route is registered for the command id.
*/
public boolean hasRoute(CommandId commandId_)
{
return _routes.containsKey(commandId_);
}
/**
* Removes all routes for subscriptions.
*/
public void unsubscribeAll()
{
ArrayList subs = new ArrayList(_routes.size());
for (Map.Entry entry : _routes.entrySet())
{
if (entry.getValue().isTerminationAck(0)) subs.add(entry.getKey());
}
for (CommandId subId : subs)
{
removeRoute(subId);
}
}
/**
* Removes all routes from self.
*/
public void clear()
{
_routes.clear();
}
/**
* Deliver a message that is known already to be an Ack. Coordinates the
* removal of routes based on the ack received and the original message type.
* @param ackMessage_ The Message to deliver.
* @param ackType_ The ack type from that message.
* @return The number of message deliveries that occurred
* @throws Exception Any exception from user message handlers.
*/
public int deliverAck(Message ackMessage_, int ackType_) throws Exception
{
assert(ackMessage_.getCommand() == Message.Command.Ack);
assert(ackType_ != Message.AckType.None);
int messagesDelivered = 0;
if(ackMessage_.getCommandId(_key))
{
messagesDelivered += _deliverAck(ackMessage_, ackType_, _key);
}
if(ackMessage_.getQueryId(_key))
{
if(messagesDelivered==0)
{
messagesDelivered += _deliverAck(ackMessage_, ackType_, _key);
} else {
_processAckForRemoval(ackType_, _key);
}
}
if(ackMessage_.getSubId(_key))
{
if(messagesDelivered == 0)
{
messagesDelivered += _deliverAck(ackMessage_, ackType_, _key);
} else {
_processAckForRemoval(ackType_, _key);
}
}
return messagesDelivered;
}
/**
* Delivers a data message (not an Ack) to the registered route.
* Uses the commandID, subID, and queryID to find a route and
* deliver to the first one found. This method is optimized for speed
* and does not attempt to examine ack types for removal of routes.
* @param dataMessage_ The non-ack message to deliver.
* @return The number of deliveries performed
* @throws Exception Any exception thrown by the user message handler.
*/
public int deliverData(Message dataMessage_) throws Exception
{
assert(dataMessage_.getCommand() != Message.Command.Ack);
int messagesDelivered = 0;
if(messagesDelivered==0 && dataMessage_.getQueryId(_key))
{
messagesDelivered += deliverData(dataMessage_, _key);
}
if(dataMessage_.getCommandId(_key))
{
messagesDelivered += deliverData(dataMessage_, _key);
}
if(messagesDelivered==0 && dataMessage_.getSubId(_key))
{
messagesDelivered += deliverData(dataMessage_, _key);
}
return messagesDelivered;
}
/**
* Delivers a data message using a specific command ID from the message. Optimized
* for speed and does not attempt to examine the message for auto-removal of routes
* @param dataMessage_ The message to deliver.
* @param commandId_ The command ID which will be used to lookup the delivery route
* @return The number of deliveries performed
* @throws Exception Any exception returned thrown by the message handler.
*/
public int deliverData(Message dataMessage_, CommandId commandId_) throws Exception
{
assert(dataMessage_.getCommand() != Message.Command.Ack);
int messagesDelivered = 0;
MessageRoute route = _routes.get(commandId_);
if(route!=null)
{
messagesDelivered += route.deliverData(dataMessage_);
}
return messagesDelivered;
}
//////////////// INTERNAL METHODS AND CLASSES ///////////////////
static private class MessageRoute
{
MessageHandler _messageHandler;
int _systemAcks, _requestedAcks, _terminationAck;
public MessageRoute(MessageHandler messageHandler_, int requestedAcks_,
int systemAcks_, boolean isSubscribe_)
{
_messageHandler = messageHandler_;
_systemAcks = systemAcks_;
_requestedAcks = requestedAcks_;
// For non-subscriptions we autoremove a handler when the termination ack comes in.
if(!isSubscribe_)
{
// compute the termination ack we're looking for
// make terminationAck the highest bit set in all acks
int bitCounter = requestedAcks_ | systemAcks_;
while(bitCounter > 0) { bitCounter>>=1; _terminationAck = _terminationAck>0?2*_terminationAck:1; }
}
}
public int deliverAck(Message message_, int ackType_) throws Exception
{
// If it was a not a requested ack, do not deliver.
if( (_requestedAcks & ackType_) == 0) return 0;
_messageHandler.invoke(message_);
return 1;
}
public boolean isTerminationAck(int ackType_)
{
return ackType_ == _terminationAck;
}
public int deliverData(Message message_) throws Exception
{
_messageHandler.invoke(message_);
return 1;
}
public MessageHandler getMessageHandler()
{
return _messageHandler;
}
}
private int _deliverAck(Message ackMessage_, int ackType_, CommandId commandId_) throws Exception
{
int messagesDelivered = 0;
MessageRoute route = _routes.get(commandId_);
if(route!=null)
{
messagesDelivered += route.deliverAck(ackMessage_, ackType_);
if(route.isTerminationAck(ackType_))
{
_routes.remove(commandId_);
}
}
return messagesDelivered;
}
private void _processAckForRemoval(int ackType_, CommandId commandId_)
{
MessageRoute route = _routes.get(commandId_);
if(route != null && route.isTerminationAck(ackType_))
{
_routes.remove(commandId_);
}
}
/////////// MEMBER VARIABLES FOR MessageRouter /////////////
// Storage for current routes.
ConcurrentHashMap _routes = new ConcurrentHashMap();
// Cached CommandId object used for retrieval of command Ids.
CommandId _key = new CommandId();
}