From e9469280c15ecfb06f4bf921e14f0cff45c3fe66 Mon Sep 17 00:00:00 2001 From: bebopagogo Date: Sun, 1 Mar 2020 12:21:58 -0500 Subject: [PATCH] extended NORM_SEND_ERROR notification and added info-only (0 size files, etc) object support --- examples/normMsgr.cpp | 3 +- include/normSession.h | 8 +- src/common/normApi.cpp | 294 +++++++----------- src/common/normObject.cpp | 9 +- src/common/normSegment.cpp | 3 +- src/common/normSession.cpp | 60 ++-- .../navy/nrl/norm/enums/NormEventType.java | 2 +- 7 files changed, 163 insertions(+), 216 deletions(-) diff --git a/examples/normMsgr.cpp b/examples/normMsgr.cpp index 7e6dc67..e21bafc 100644 --- a/examples/normMsgr.cpp +++ b/examples/normMsgr.cpp @@ -1051,10 +1051,9 @@ int main(int argc, char* argv[]) { fprintf(stderr, "normMsgr error: unable to open NORM session\n"); NormDestroyInstance(normInstance); - return false; + return -1; } - if (silentReceiver) normMsgr.SetSilentReceiver(true); if (txloss > 0.0) normMsgr.SetTxLoss(txloss); diff --git a/include/normSession.h b/include/normSession.h index 03f076f..f444cca 100644 --- a/include/normSession.h +++ b/include/normSession.h @@ -57,7 +57,9 @@ class NormController CC_INACTIVE, // posted when no cc feedback and min rate reached ACKING_NODE_NEW, SEND_ERROR, - USER_TIMEOUT + USER_TIMEOUT, + // The ones below here are not exposed via the NORM API + SEND_OK, }; virtual void Notify(NormController::Event event, @@ -267,6 +269,9 @@ class NormSession } void SetTxRateBounds(double rateMin, double rateMax); + void ClearSendError() + {posted_send_error = false;} + double BackoffFactor() {return backoff_factor;} void SetBackoffFactor(double value) @@ -778,6 +783,7 @@ class NormSession int flush_count; bool posted_tx_queue_empty; bool posted_tx_rate_changed; + bool posted_send_error; // For postive acknowledgement collection NormNodeTree acking_node_tree; diff --git a/src/common/normApi.cpp b/src/common/normApi.cpp index 7379d96..09e4ed9 100644 --- a/src/common/normApi.cpp +++ b/src/common/normApi.cpp @@ -116,51 +116,12 @@ class NormInstance : public NormController return static_cast(session.GetSessionMgr().GetController()); } - class Notification + class Notification : public ProtoList::Item { public: NormEvent event; - - void Append(Notification* n) {next = n;} - - Notification* GetNext() const {return next;} - - class Queue - { - public: - Queue(); - ~Queue(); - bool IsEmpty() const {return (NULL == head);} - void Destroy(); - void Append(Notification* n) - { - n->Append(NULL); - if (tail) - tail->Append(n); - else - head = n; - tail = n; - } - Notification* RemoveHead() - { - Notification* n = head; - if (n) - { - head = n->GetNext(); - tail = head ? tail : NULL; - } - return n; - } - Notification* GetHead() {return head;} - void SetTail(Notification* n) {tail = n;} - private: - Notification* head; - Notification* tail; - - }; // end class NormInstance::Notification::Queue - - private: - Notification* next; + + class Queue : public ProtoListTemplate {}; }; // end class NormInstance::Notification ProtoDispatcher dispatcher; @@ -193,25 +154,6 @@ class NormInstance : public NormController #endif // if/else WIN32/UNIX }; // end class NormInstance -//////////////////////////////////////////////////// -// NormInstance::Notification::Queue implementation -NormInstance::Notification::Queue::Queue() - : head(NULL), tail(NULL) -{ - -} - - -NormInstance::Notification::Queue::~Queue() -{ - Destroy(); -} - -void NormInstance::Notification::Queue::Destroy() -{ - Notification* n; - while ((n = RemoveHead())) delete n; -} // end NormInstance::Notification::Queue::Destroy() //////////////////////////////////////////////////// // NormInstance implementation @@ -274,17 +216,26 @@ void NormInstance::Notify(NormController::Event event, class NormNode* node, class NormObject* object) { + switch (event) + { + case SEND_OK: + // Purge any pending NORM_SEND_ERROR notifications for session + PurgeNotifications(session, NORM_SEND_ERROR); + return; + default: + break; + } + // (TBD) set a limit on how many pending notifications // we allow to queue up (it could be large and probably // we could base it on how much memory space the pending // notifications are allowed to consume. - Notification* n = notify_pool.RemoveHead(); - if (!n) + Notification* next = notify_pool.RemoveHead(); + if (NULL == next) { - if (!(n = new Notification)) + if (NULL == (next = new Notification)) { - PLOG(PL_FATAL, "NormInstance::Notify() new Notification error: %s\n", - GetErrorString()); + PLOG(PL_FATAL, "NormInstance::Notify() new Notification error: %s\n", GetErrorString()); return; } } @@ -306,7 +257,7 @@ void NormInstance::Notify(NormController::Event event, if (!stream->Accept(size.LSB(), true)) { PLOG(PL_FATAL, "NormInstance::Notify() stream accept error\n"); - notify_pool.Append(n); + notify_pool.Append(*next); return; } // By setting a non-zero "block pool threshold", this @@ -375,6 +326,7 @@ void NormInstance::Notify(NormController::Event event, { // we're ignoring files PLOG(PL_DETAIL, "NormInstance::Notify() warning: receive file but no cache directory set, so ignoring file\n"); + notify_pool.Append(*next); return; } break; @@ -387,7 +339,8 @@ void NormInstance::Notify(NormController::Event event, if (NULL == dataPtr) { PLOG(PL_FATAL, "NormInstance::Notify(RX_OBJECT_NEW) new dataPtr error: %s\n", - GetErrorString()); + GetErrorString()); + notify_pool.Append(*next); return; } // Note that the "true" parameter means the @@ -395,11 +348,17 @@ void NormInstance::Notify(NormController::Event event, // data on object deletion, so the app should // use NormDataDetachData() to keep the received // data (or copy it before the data object is deleted) - dataObj->Accept(dataPtr, dataLen, true); + if (!dataObj->Accept(dataPtr, dataLen, true)) + { + PLOG(PL_FATAL, "NormInstance::Notify() data object accept error\n"); + notify_pool.Append(*next); + return; + } break; } default: // This shouldn't occur + notify_pool.Append(*next); return; } // end switch(object->GetType()) break; @@ -416,11 +375,11 @@ void NormInstance::Notify(NormController::Event event, ((NormNode*)node)->Retain(); bool doNotify = notify_queue.IsEmpty(); - n->event.type = (NormEventType)event; - n->event.session = session; - n->event.sender = node; - n->event.object = object; - notify_queue.Append(n); + next->event.type = (NormEventType)event; + next->event.session = session; + next->event.sender = node; + next->event.object = object; + notify_queue.Append(*next); if (doNotify) { @@ -428,7 +387,7 @@ void NormInstance::Notify(NormController::Event event, if (0 == SetEvent(notify_event)) { PLOG(PL_ERROR, "NormInstance::Notify() SetEvent() error: %s\n", - GetErrorString()); + GetErrorString()); } #else char byte = 0; @@ -437,7 +396,7 @@ void NormInstance::Notify(NormController::Event event, if ((EINTR != errno) && (EAGAIN != errno)) { PLOG(PL_FATAL, "NormInstance::Notify() write() error: %s\n", - GetErrorString()); + GetErrorString()); break; } } @@ -449,35 +408,25 @@ void NormInstance::Notify(NormController::Event event, void NormInstance::PurgeObjectNotifications(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID == objectHandle) return; - Notification* prev = NULL; - Notification* next = notify_queue.GetHead(); - while (next) + + Notification::Queue::Iterator iterator(notify_queue); + Notification* next; + while (NULL != (next = iterator.GetNextItem())) { if (objectHandle == next->event.object) { // "Release" the previously-retained object handle ((NormObject*)objectHandle)->Release(); - // Remove this notification from queue and return to pool - Notification* current = next; - next = next->GetNext(); - if (NULL != prev) - prev->Append(next); - else - notify_queue.RemoveHead(); - if (NULL == next) notify_queue.SetTail(prev); - notify_pool.Append(current); - } - else - { - prev = next; - next = next->GetNext(); + // Remove from queue and put in pool + notify_queue.Remove(*next); + notify_pool.Append(*next); } } if ((NULL != previous_notification) && (objectHandle == previous_notification->event.object)) { // "Release" any previously-retained object or node handle ((NormObject*)(previous_notification->event.object))->Release(); - notify_pool.Append(previous_notification); + notify_pool.Append(*previous_notification); previous_notification = NULL; } // TBD - check if event queue is emptied and reset event/fd @@ -487,28 +436,17 @@ void NormInstance::PurgeObjectNotifications(NormObjectHandle objectHandle) void NormInstance::PurgeNodeNotifications(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID == nodeHandle) return; - Notification* prev = NULL; - Notification* next = notify_queue.GetHead(); - while (next) + Notification::Queue::Iterator iterator(notify_queue); + Notification* next; + while (NULL != (next = iterator.GetNextItem())) { if (nodeHandle == next->event.sender) { // "Release" the previously-retained object handle ((NormNode*)nodeHandle)->Release(); // Remove this notification from queue and return to pool - Notification* current = next; - next = next->GetNext(); - if (NULL != prev) - prev->Append(next); - else - notify_queue.RemoveHead(); - if (NULL == next) notify_queue.SetTail(prev); - notify_pool.Append(current); - } - else - { - prev = next; - next = next->GetNext(); + notify_queue.Remove(*next); + notify_pool.Append(*next); } } if ((NULL != previous_notification) && (nodeHandle == previous_notification->event.sender)) @@ -518,7 +456,7 @@ void NormInstance::PurgeNodeNotifications(NormNodeHandle nodeHandle) ((NormObject*)(previous_notification->event.object))->Release(); else ((NormNode*)(previous_notification->event.sender))->Release(); - notify_pool.Append(previous_notification); + notify_pool.Append(*previous_notification); previous_notification = NULL; } if (notify_queue.IsEmpty()) ResetNotificationEvent(); @@ -527,31 +465,20 @@ void NormInstance::PurgeNodeNotifications(NormNodeHandle nodeHandle) void NormInstance::PurgeSessionNotifications(NormSessionHandle sessionHandle) { if (NORM_SESSION_INVALID == sessionHandle) return; - Notification* prev = NULL; - Notification* next = notify_queue.GetHead(); - while (NULL != next) + Notification::Queue::Iterator iterator(notify_queue); + Notification* next; + while (NULL != (next = iterator.GetNextItem())) { if (next->event.session == sessionHandle) { + if (NORM_OBJECT_INVALID != next->event.object) + ((NormObject*)next->event.object)->Release(); + else if (NORM_NODE_INVALID != next->event.sender) + ((NormNode*)next->event.sender)->Release(); // Remove this notification from queue and return to pool - Notification* current = next; - next = next->GetNext(); - if (NULL != prev) - prev->Append(next); - else - notify_queue.RemoveHead(); - if (NULL == next) notify_queue.SetTail(prev); - if (NORM_OBJECT_INVALID != current->event.object) - ((NormObject*)current->event.object)->Release(); - else if (NORM_NODE_INVALID != current->event.sender) - ((NormNode*)current->event.sender)->Release(); - notify_pool.Append(current); - } - else - { - prev = next; - next = next->GetNext(); - } + notify_queue.Remove(*next); + notify_pool.Append(*next); + } } if ((NULL != previous_notification) && (sessionHandle == previous_notification->event.session)) { @@ -560,7 +487,7 @@ void NormInstance::PurgeSessionNotifications(NormSessionHandle sessionHandle) ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); - notify_pool.Append(previous_notification); + notify_pool.Append(*previous_notification); previous_notification = NULL; } if (notify_queue.IsEmpty()) ResetNotificationEvent(); @@ -570,32 +497,21 @@ void NormInstance::PurgeSessionNotifications(NormSessionHandle sessionHandle) void NormInstance::PurgeNotifications(NormSessionHandle sessionHandle, NormEventType eventType) { if (NORM_SESSION_INVALID == sessionHandle) return; - Notification* prev = NULL; - Notification* next = notify_queue.GetHead(); - while (next) + Notification::Queue::Iterator iterator(notify_queue); + Notification* next; + while (NULL != (next = iterator.GetNextItem())) { if ((next->event.session == sessionHandle) && (next->event.type == eventType)) { + if (NORM_OBJECT_INVALID != next->event.object) + ((NormObject*)next->event.object)->Release(); + else if (NORM_NODE_INVALID != next->event.sender) + ((NormNode*)next->event.sender)->Release(); // Remove this notification from queue and return to pool - Notification* current = next; - next = next->GetNext(); - if (NULL != prev) - prev->Append(next); - else - notify_queue.RemoveHead(); - if (NULL == next) notify_queue.SetTail(prev); - if (NORM_OBJECT_INVALID != current->event.object) - ((NormObject*)current->event.object)->Release(); - else if (NORM_NODE_INVALID != current->event.sender) - ((NormNode*)current->event.sender)->Release(); - notify_pool.Append(current); + notify_queue.Remove(*next); + notify_pool.Append(*next); } - else - { - prev = next; - next = next->GetNext(); - } } if (notify_queue.IsEmpty()) ResetNotificationEvent(); } // end NormInstance::PurgeNotifications() @@ -611,19 +527,19 @@ bool NormInstance::GetNextEvent(NormEvent* theEvent) ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); - notify_pool.Append(previous_notification); + notify_pool.Append(*previous_notification); previous_notification = NULL; } - Notification* n; - while (NULL != (n = notify_queue.RemoveHead())) + Notification* next; + while (NULL != (next = notify_queue.RemoveHead())) { - switch (n->event.type) + switch (next->event.type) { case NORM_EVENT_INVALID: if (!notify_queue.IsEmpty()) { // Discard this invalid event and get next one - notify_pool.Append(n); + notify_pool.Append(*next); continue; } break; @@ -631,20 +547,25 @@ bool NormInstance::GetNextEvent(NormEvent* theEvent) { // reset update event notification for non-streams // (NormStreamRead() takes care of streams) - NormObject* obj = ((NormObject*)n->event.object); - if (!obj->IsStream()) - obj->SetNotifyOnUpdate(true); + NormObject* obj = ((NormObject*)next->event.object); + if (!obj->IsStream()) obj->SetNotifyOnUpdate(true); + break; + } + case NORM_SEND_ERROR: + { + NormSession* session = (NormSession*)next->event.session; + session->ClearSendError(); break; } default: break; } - break; + break; } - if (NULL != n) + if (NULL != next) { - previous_notification = n; // keep dispatched event for garbage collection - if (NULL != theEvent) *theEvent = n->event; + previous_notification = next; // keep dispatched event for garbage collection + if (NULL != theEvent) *theEvent = next->event; } else if (NULL != theEvent) { @@ -653,9 +574,8 @@ bool NormInstance::GetNextEvent(NormEvent* theEvent) theEvent->sender = NORM_NODE_INVALID; theEvent->object = NORM_OBJECT_INVALID; } - if (notify_queue.IsEmpty()) - ResetNotificationEvent(); - return (NULL != n); + if (notify_queue.IsEmpty()) ResetNotificationEvent(); + return (NULL != next); } // end NormInstance::GetNextEvent() bool NormInstance::WaitForEvent() @@ -737,7 +657,7 @@ void NormInstance::ReleasePreviousEvent() ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); - notify_pool.Append(previous_notification); + notify_pool.Append(*previous_notification); previous_notification = NULL; } } // end NormInstance::ReleasePreviousEvent() @@ -785,18 +705,18 @@ void NormInstance::Shutdown() ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); - notify_pool.Append(previous_notification); + notify_pool.Append(*previous_notification); previous_notification = NULL; } - Notification* n; - while ((n = notify_queue.RemoveHead())) + Notification* next; + while (NULL != (next = notify_queue.RemoveHead())) { - switch (n->event.type) + switch (next->event.type) { case NORM_RX_OBJECT_NEW: { - NormObject* obj = (NormObject*)n->event.object; + NormObject* obj = (NormObject*)next->event.object; switch (obj->GetType()) { case NormObject::FILE: @@ -810,30 +730,33 @@ void NormInstance::Shutdown() default: break; } - if (NORM_OBJECT_INVALID != n->event.object) - ((NormObject*)(n->event.object))->Release(); - else if (NORM_NODE_INVALID != n->event.sender) - ((NormNode*)(n->event.sender))->Release(); - delete n; + if (NORM_OBJECT_INVALID != next->event.object) + ((NormObject*)(next->event.object))->Release(); + else if (NORM_NODE_INVALID != next->event.sender) + ((NormNode*)(next->event.sender))->Release(); + delete next; } notify_pool.Destroy(); } // end NormInstance::Shutdown() +/* +This function doesn't make sense? UINT32 NormInstance::CountCompletedObjects(NormSession* session) { UINT32 result = 0UL; - Notification* n = notify_queue.GetHead(); - while (NULL != n) + Notification::Queue::Iterator iterator(notify_queue); + Notification* next; + while (NULL != (next = iterator.GetNextItem())) { - if ((session == n->event.session) && - (NORM_RX_OBJECT_COMPLETED == n->event.type)) + if ((session == next->event.session) && + (NORM_RX_OBJECT_COMPLETED == next->event.type)) { result ++; } - n = n->GetNext(); } return result; } // end NormInstance::CountCompletedObjects() +*/ ////////////////////////////////////////////////////////////////////////// // NORM API FUNCTION IMPLEMENTATIONS @@ -3051,7 +2974,7 @@ void NormNodeRelease(NormNodeHandle nodeHandle) // is done so that this current approach is not very costly) // - +/* // Not sure why this function exists. It just counts the number // of RX_OBJECT_COMPLETED events currently in notification queue // which is a temporary and transitory thing??? @@ -3068,3 +2991,4 @@ UINT32 NormCountCompletedObjects(NormSessionHandle sessionHandle) } return result; } // end NormCountCompletedObjects() +*/ diff --git a/src/common/normObject.cpp b/src/common/normObject.cpp index 96d6226..77218d9 100644 --- a/src/common/normObject.cpp +++ b/src/common/normObject.cpp @@ -201,7 +201,9 @@ bool NormObject::Open(const NormObjectSize& objectSize, pending_mask.SetBits(0, numBlocks.LSB()); // Compute FEC block structure per NORM Protocol Spec Section 5.1.1 // (Note NormObjectSize divide operator always rounds _up_, i.e., ceil(numSegments/numBlocks)) - NormObjectSize largeBlockSize = numSegments / numBlocks; + NormObjectSize largeBlockSize; + if ((0 != numBlocks.MSB()) || (0 != numBlocks.LSB())) + largeBlockSize = numSegments / numBlocks; ASSERT(0 == largeBlockSize.MSB()); large_block_size = largeBlockSize.LSB(); if (numSegments == (numBlocks*largeBlockSize)) @@ -2336,7 +2338,7 @@ bool NormFileObject::Open(const char* thePath, if (file.Open(thePath, O_RDONLY)) { NormObjectSize::Offset size = file.GetSize(); - if (size) + //if (size) { if (!NormObject::Open(NormObjectSize(size), infoPtr, @@ -2352,12 +2354,13 @@ bool NormFileObject::Open(const char* thePath, return false; } } + /* else { PLOG(PL_FATAL, "NormFileObject::Open() send file.GetSize() error!\n"); file.Close(); return false; - } + }*/ } else { diff --git a/src/common/normSegment.cpp b/src/common/normSegment.cpp index 0cb468c..822c17d 100644 --- a/src/common/normSegment.cpp +++ b/src/common/normSegment.cpp @@ -701,7 +701,8 @@ bool NormBlockBuffer::Init(unsigned long rangeMax, unsigned long tableSize, UINT { Destroy(); // Make sure tableSize is greater than 0 and 2^n - if (!rangeMax || !tableSize) + //if (!rangeMax || !tableSize) + if (0 == tableSize) { PLOG(PL_FATAL, "NormBlockBuffer::Init() bad range(%lu) or tableSize(%lu)\n", rangeMax, tableSize); diff --git a/src/common/normSession.cpp b/src/common/normSession.cpp index 490899c..6cc0855 100644 --- a/src/common/normSession.cpp +++ b/src/common/normSession.cpp @@ -53,7 +53,7 @@ NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId) tx_cache_count_min(DEFAULT_TX_CACHE_MIN), tx_cache_count_max(DEFAULT_TX_CACHE_MAX), tx_cache_size_max(DEFAULT_TX_CACHE_SIZE), - posted_tx_queue_empty(false), posted_tx_rate_changed(false), + posted_tx_queue_empty(false), posted_tx_rate_changed(false), posted_send_error(false), acking_node_count(0), acking_auto_populate(TRACK_NONE), watermark_pending(false), watermark_flushes(false), tx_repair_pending(false), advertise_repairs(false), suppress_nonconfirmed(false), suppress_rate(-1.0), suppress_rtt(-1.0), @@ -4907,40 +4907,54 @@ NormSession::MessageStatus NormSession::SendMessage(NormMsg& msg) { unsigned int numBytes = msgSize; bool result = tx_socket->SendTo(msg.GetBuffer(), numBytes, msg.GetDestination()); - if (numBytes == msgSize) + + if (result) { - // Separate send/recv tracing - if (trace) + if (numBytes == msgSize) { - struct timeval currentTime; - ProtoSystemTime(currentTime); - NormTrace(currentTime, LocalNodeId(), msg, true, fecM, instId); + if (posted_send_error) + { + // Clear SEND_ERROR indication + posted_send_error = false; + Notify(NormController::SEND_OK, NULL, NULL); + } + // Separate send/recv tracing + if (trace) + { + struct timeval currentTime; + ProtoSystemTime(currentTime); + NormTrace(currentTime, LocalNodeId(), msg, true, fecM, instId); + } + // To keep track of _actual_ sent rate + sent_accumulator.Increment(msgSize); + // Update nominal packet size + nominal_packet_size += 0.01 * (((double)msgSize) - nominal_packet_size); + } + else + { + // packet not sent + tx_sequence--; + // TBD - is PL_WARN too verbose here + PLOG(PL_WARN, "NormSession::SendMessage() sendto(%s/%hu) 'blocked' warning: %s\n", + msg.GetDestination().GetHostString(), msg.GetDestination().GetPort(), GetErrorString()); + return MSG_SEND_BLOCKED; } - // To keep track of _actual_ sent rate - sent_accumulator.Increment(msgSize); - // Update nominal packet size - nominal_packet_size += 0.01 * (((double)msgSize) - nominal_packet_size); } else { // packet not sent tx_sequence--; - if (result) + PLOG(PL_WARN, "NormSession::SendMessage() sendto(%s/%hu) 'failed' warning: %s\n", + msg.GetDestination().GetHostString(), msg.GetDestination().GetPort(), GetErrorString()); + if (!posted_send_error) { - // TBD - is PL_WARN too verbose here - PLOG(PL_WARN, "NormSession::SendMessage() sendto(%s/%hu) warning: %s\n", - msg.GetDestination().GetHostString(), msg.GetDestination().GetPort(), GetErrorString()); - return MSG_SEND_BLOCKED; - } - else - { - PLOG(PL_WARN, "NormSession::SendMessage() sendto(%s/%hu) warning: %s\n", - msg.GetDestination().GetHostString(), msg.GetDestination().GetPort(), GetErrorString()); - return MSG_SEND_FAILED; + // Post a Notify(NormController::SEND_ERROR, NULL, NULL); + posted_send_error = true; + Notify(NormController::SEND_ERROR, NULL, NULL); } + return MSG_SEND_FAILED; } } - if (isProbe) { probe_pending = false; diff --git a/src/java/src/mil/navy/nrl/norm/enums/NormEventType.java b/src/java/src/mil/navy/nrl/norm/enums/NormEventType.java index 64ee2fb..3fded02 100644 --- a/src/java/src/mil/navy/nrl/norm/enums/NormEventType.java +++ b/src/java/src/mil/navy/nrl/norm/enums/NormEventType.java @@ -5,7 +5,7 @@ package mil.navy.nrl.norm.enums; * the native implementation and therefore is tied to the specific version of * NORM. * - * @author Jason Rush + * @author Jason Rush and Brian Adamson */ public enum NormEventType { NORM_EVENT_INVALID,