diff options
Diffstat (limited to 'lib/cosNotification/src')
34 files changed, 13110 insertions, 0 deletions
diff --git a/lib/cosNotification/src/CosEvent.cfg b/lib/cosNotification/src/CosEvent.cfg new file mode 100644 index 0000000000..e3399139a4 --- /dev/null +++ b/lib/cosNotification/src/CosEvent.cfg @@ -0,0 +1,20 @@ +{this, "CosEventChannelAdmin::EventChannel"}. +{{handle_info, "CosEventChannelAdmin::EventChannel"}, true}. +{this, "CosEventChannelAdmin::EventChannelFactory"}. +{{handle_info, "CosEventChannelAdmin::EventChannelFactory"}, true}. +{this, "CosEventChannelAdmin::SupplierAdmin"}. +{{handle_info, "CosEventChannelAdmin::SupplierAdmin"}, true}. +{this, "CosEventChannelAdmin::ConsumerAdmin"}. +{{handle_info, "CosEventChannelAdmin::ConsumerAdmin"}, true}. +{this, "CosEventChannelAdmin::ProxyPushSupplier"}. +{{handle_info, "CosEventChannelAdmin::ProxyPushSupplier"}, true}. +{{impl, "CosEventChannelAdmin::ProxyPushSupplier"}, "PusherSupplier_impl"}. +{this, "CosEventChannelAdmin::ProxyPullSupplier"}. +{{handle_info, "CosEventChannelAdmin::ProxyPullSupplier"}, true}. +{{impl, "CosEventChannelAdmin::ProxyPullSupplier"}, "PullerSupplier_impl"}. +{this, "CosEventChannelAdmin::ProxyPushConsumer"}. +{{handle_info, "CosEventChannelAdmin::ProxyPushConsumer"}, true}. +{{impl, "CosEventChannelAdmin::ProxyPushConsumer"}, "PusherConsumer_impl"}. +{this, "CosEventChannelAdmin::ProxyPullConsumer"}. +{{handle_info, "CosEventChannelAdmin::ProxyPullConsumer"}, true}. +{{impl, "CosEventChannelAdmin::ProxyPullConsumer"}, "PullerConsumer_impl"}. diff --git a/lib/cosNotification/src/CosNotification.cfg b/lib/cosNotification/src/CosNotification.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/cosNotification/src/CosNotification.cfg diff --git a/lib/cosNotification/src/CosNotification.idl b/lib/cosNotification/src/CosNotification.idl new file mode 100644 index 0000000000..e080b44b0c --- /dev/null +++ b/lib/cosNotification/src/CosNotification.idl @@ -0,0 +1,146 @@ +#ifndef _COS_NOTIFICATION_IDL_ +#define _COS_NOTIFICATION_IDL_ + +#pragma prefix "omg.org" + +#include"CosEventChannelAdmin.idl" +#include"CosEventComm.idl" + +module CosNotification { + typedef string Istring; + typedef Istring PropertyName; + typedef any PropertyValue; + struct Property { + PropertyName name; + PropertyValue value; + }; + typedef sequence<Property> PropertySeq; + // The following are the same, but serve different purposes. + typedef PropertySeq OptionalHeaderFields; + typedef PropertySeq FilterableEventBody; + typedef PropertySeq QoSProperties; + typedef PropertySeq AdminProperties; + struct EventType { + string domain_name; + string type_name; + }; + typedef sequence<EventType> EventTypeSeq; + struct PropertyRange { + PropertyValue low_val; + PropertyValue high_val; + }; + struct NamedPropertyRange { + PropertyName name; + PropertyRange range; + }; + + typedef sequence<NamedPropertyRange> NamedPropertyRangeSeq; + + enum QoSError_code { + UNSUPPORTED_PROPERTY, + UNAVAILABLE_PROPERTY, + UNSUPPORTED_VALUE, + UNAVAILABLE_VALUE, + BAD_PROPERTY, + BAD_TYPE, + BAD_VALUE + }; + + struct PropertyError { + QoSError_code code; + PropertyName name; + PropertyRange available_range; + }; + + typedef sequence<PropertyError> PropertyErrorSeq; + exception UnsupportedQoS { PropertyErrorSeq qos_err; }; + exception UnsupportedAdmin { PropertyErrorSeq admin_err; }; + + // Define the Structured Event structure + struct FixedEventHeader { + EventType event_type; + string event_name; + }; + struct EventHeader { + FixedEventHeader fixed_header; + OptionalHeaderFields variable_header; + }; + + struct StructuredEvent { + EventHeader header; + FilterableEventBody filterable_data; + any remainder_of_body; + }; // StructuredEvent + + typedef sequence<StructuredEvent> EventBatch; + + // The following constant declarations define the standard + // QoS property names and the associated values each property + // can take on. The name/value pairs for each standard property + // are grouped, beginning with a string constant defined for the + // property name, followed by the values the property can take on. + const string EventReliability = "EventReliability"; + const short BestEffort = 0; + const short Persistent = 1; + const string ConnectionReliability = "ConnectionReliability"; + + // Can take on the same values as EventReliability + const string Priority = "Priority"; + const short LowestPriority = -32767; + const short HighestPriority = 32767; + const short DefaultPriority = 0; + const string StartTime = "StartTime"; + + // StartTime takes a value of type TimeBase::UtcT. + const string StopTime = "StopTime"; + // StopTime takes a value of type TimeBase::UtcT. + const string Timeout = "Timeout"; + // Timeout takes on a value of type TimeBase::TimeT + const string OrderPolicy = "OrderPolicy"; + const short AnyOrder = 0; + const short FifoOrder = 1; + const short PriorityOrder = 2; + const short DeadlineOrder = 3; + const string DiscardPolicy = "DiscardPolicy"; + // DiscardPolicy takes on the same values as OrderPolicy, plus + const short LifoOrder = 4; + const short RejectNewEvents = 5; + const string MaximumBatchSize = "MaximumBatchSize"; + // MaximumBatchSize takes on a value of type long + const string PacingInterval = "PacingInterval"; + // PacingInterval takes on a value of type TimeBase::TimeT + const string StartTimeSupported = "StartTimeSupported"; + // StartTimeSupported takes on a boolean value + const string StopTimeSupported = "StopTimeSupported"; + // StopTimeSupported takes on a boolean value + const string MaxEventsPerConsumer = "MaxEventsPerConsumer"; + // MaxEventsPerConsumer takes on a value of type long + + interface QoSAdmin { + QoSProperties get_qos(); + void set_qos ( in QoSProperties qos) + raises ( UnsupportedQoS ); + void validate_qos (in QoSProperties required_qos, + out NamedPropertyRangeSeq available_qos ) + raises ( UnsupportedQoS ); + }; // QosAdmin + + // Admin properties are defined in similar manner as QoS + // properties. The only difference is that these properties + // are related to channel administration policies, as opposed + // message quality of service + const string MaxQueueLength = "MaxQueueLength"; + // MaxQueueLength takes on a value of type long + const string MaxConsumers = "MaxConsumers"; + // MaxConsumers takes on a value of type long + const string MaxSuppliers = "MaxSuppliers"; + // MaxSuppliers takes on a value of type long + interface AdminPropertiesAdmin { + AdminProperties get_admin(); + void set_admin (in AdminProperties admin) + raises ( UnsupportedAdmin); + };// AdminPropertiesAdmin +}; // CosNotification + +#endif /* ifndef _COS_NOTIFICATION_IDL_ */ + diff --git a/lib/cosNotification/src/CosNotification_Common.erl b/lib/cosNotification/src/CosNotification_Common.erl new file mode 100644 index 0000000000..0e0f1da0d5 --- /dev/null +++ b/lib/cosNotification/src/CosNotification_Common.erl @@ -0,0 +1,1210 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%-------------------------------------------------------------------- +%% File : CosNotification_Common.erl +%% Purpose : +%%-------------------------------------------------------------------- + +-module('CosNotification_Common'). + + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%% External MISC +-export([get_option/3, + create_name/2, + create_name/1, + create_id/0, + create_id/1, + is_debug_compiled/0, + type_check/2, + send_stubborn/5, + create_link/3, + disconnect/3, + do_disconnect/3, + notify/1]). + +%% Internal AdminProperties +-export([init_adm/1, + set_adm/2, + 'MaxQueueLength'/6, + 'MaxConsumers'/6, + 'MaxSuppliers'/6]). +%% Internal QoS +-export([init_qos/1, + set_qos/5, + validate_qos/5, + validate_event_qos/2, + 'EventReliability'/6, + 'ConnectionReliability'/6, + 'Priority'/6, + 'StartTimeSupported'/6, + 'StopTimeSupported'/6, + 'Timeout'/6, + 'OrderPolicy'/6, + 'DiscardPolicy'/6, + 'MaximumBatchSize'/6, + 'PacingInterval'/6, + 'MaxEventsPerConsumer'/6]). + +%%--------------- DEFINITIONS OF CONSTANTS ------------------- +%%--------------- EXTERNAL MISC FUNCTIONS -------------------- +%%------------------------------------------------------------ +%% function : create_link +%% Arguments: Module - which Module to call +%% Env/ArgList - ordinary oe_create arguments. +%% Returns : +%% Exception: +%% Effect : Necessary since we want the supervisor to be a +%% 'simple_one_for_one'. Otherwise, using for example, +%% 'one_for_one', we have to call supervisor:delete_child +%% to remove the childs startspecification from the +%% supervisors internal state. +%%------------------------------------------------------------ +create_link(Module, Env, ArgList) -> + Module:oe_create_link(Env, ArgList). + +%%-----------------------------------------------------------% +%% function : get_option +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%------------------------------------------------------------ +get_option(Key, OptionList, DefaultList) -> + case lists:keysearch(Key, 1, OptionList) of + {value,{Key,Value}} -> + Value; + _ -> + case lists:keysearch(Key, 1, DefaultList) of + {value,{Key,Value}} -> + Value; + _-> + {error, "Invalid option"} + end + end. +%%-----------------------------------------------------------% +%% function : create_name/2 +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%------------------------------------------------------------ +create_name(Name,Type) -> + {MSec, Sec, USec} = erlang:now(), + lists:concat(['oe_',node(),'_',Type,'_',Name,'_',MSec, '_', Sec, '_', USec]). + +%%-----------------------------------------------------------% +%% function : create_name/1 +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%------------------------------------------------------------ +create_name(Type) -> + {MSec, Sec, USec} = erlang:now(), + lists:concat(['oe_',node(),'_',Type,'_',MSec, '_', Sec, '_', USec]). + +%%------------------------------------------------------------ +%% function : create_id/0 +%% Arguments: - +%% Returns : id (long) =/= 0 +%% Both default Admin:s have the unique id 0 (OMG spec, 98-11-01, +%% Notification p 148), hence, we may not return 0. +%% Exception: +%% Purpose : Throughout the CosNotification service we use, +%% according to the OMG specification, id:s (long), +%% which must be "unique", to retrieve object references. +%% For example: CosNotifyChannelAdmin::ChannelId/AdminID. +%%------------------------------------------------------------ +create_id(-1) -> + 1; +create_id( 2147483647) -> + -2147483648; +create_id(OldID) -> + OldID+1. + +create_id() -> + {_A,_B,C}=now(), + C. + +%%-----------------------------------------------------------% +%% function : type_check +%% Arguments: Obj - objectrefernce to test. +%% Mod - Module which contains typeID/0. +%% Returns : 'ok' or raises exception. +%% Effect : +%%------------------------------------------------------------ +type_check(Obj, Mod) -> + case cosNotificationApp:type_check() of + false -> + ok; + _ -> + case catch corba_object:is_a(Obj,Mod:typeID()) of + true -> + ok; + false -> + orber:dbg("[~p] CosNotification_Common:type_check(~p);~n" + "The supplied Object is not or does not inherrit from: ~p", + [?LINE, Obj, Mod], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}); + {'EXCEPTION', E} -> + orber:dbg("[~p] CosNotification_Common:type_check(~p, ~p);~n" + "Failed due to: ~p", + [?LINE, Obj, Mod, E], ?DEBUG_LEVEL), + corba:raise(E); + What -> + orber:dbg("[~p] CosNotification_Common:type_check(~p, ~p);~n" + "Failed due to: ~p", + [?LINE, Obj, Mod, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end + end. + + +%%-----------------------------------------------------------% +%% function : notify +%% Arguments: Items - [Item] +%% Item - {proxy, IOR} | {client, IOR} | {reason, term()} +%% Returns : 'ok' or raises exception. +%% Effect : +%%------------------------------------------------------------ +notify(Items) -> + case cosNotificationApp:notify() of + false -> + ok; + Module -> + catch Module:terminated(Items), + ok + end. + + +%%------------------------------------------------------------ +%% function : send_stubborn +%% Arguments: M - module +%% F - function +%% A - arguments +%% MaxR - Maximum no retries +%% Wait - sleep Wait seconds before next try. +%% Returns : see effect +%% Exception: +%% Effect : Retries repeatidly untill anything else besides +%% 'EXIT', 'COMM_FAILURE' or 'OBJECT_NOT_EXIST' +%%------------------------------------------------------------ + +send_stubborn(M, F, A, MaxR, Wait) when is_list(A) -> + send_stubborn(M, F, A, MaxR, Wait, 0); +send_stubborn(M, F, A, MaxR, Wait) -> + send_stubborn(M, F, [A], MaxR, Wait, 0). +send_stubborn(M, F, A, MaxR, _Wait, MaxR) -> + orber:dbg("[~p] CosNotification_Common:send_stubborn( ~p ~p ~p ~p).~n" + "Failed to deliver the event.~n", [?LINE, M,F,A,MaxR], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}); +send_stubborn(M, F, A, MaxR, Wait, Times) -> + ?debug_print("~p:~p(~p) # of retries: ~p~n", [M,F,A, Times]), + case catch apply(M,F,A) of + {'EXCEPTION', E} when is_record(E, 'COMM_FAILURE')-> + NewTimes = Times +1, + timer:sleep(Wait), + send_stubborn(M, F, A, MaxR, Wait, NewTimes); + {'EXIT', _} -> + NewTimes = Times +1, + timer:sleep(Wait), + send_stubborn(M, F, A, MaxR, Wait, NewTimes); + Other -> + Other + end. + + +%%-----------------------------------------------------------% +%% function : disconnect +%% Arguments: Module - one of the interfaces defined in CosEventComm. +%% Function - the appropriate disconnect function. +%% Object - the client object reference. +%% Returns : ok +%% Exception: +%% Effect : If the process would try to diconnect itself it could +%% result in a deadlock. Hence, we spawn a new process to do it. +%%------------------------------------------------------------ +disconnect(Module, Function, Object) -> + spawn(?MODULE, do_disconnect, [Module, Function, Object]), + ok. + +do_disconnect(Module, Function, Object) -> + catch Module:Function(Object), + ?DBG("Disconnect ~p:~p(..).~n", [Module, Function]), + ok. + + + +%%------------------------------------------------------------ +%% function : is_debug_compiled +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%------------------------------------------------------------ + +-ifdef(debug). + is_debug_compiled() -> true. +-else. + is_debug_compiled() -> false. +-endif. + + +%%------------------------------------------------------------ +%%--------------- AdminPropertiesAdmin ----------------------- +%%------------------------------------------------------------ +%%------------------------------------------------------------ +%% function : init_adm +%% Arguments: Wanted - requested Admins to be set. +%% Returns : #'CosNotification_UnsupportedAdmin'{} | +%% {NewAdmProperties, [MaxQ, MaxC, MaxS]} +%% Effect : may only be used when creating a channel!!!!!!!! +%%------------------------------------------------------------ +init_adm(Wanted) -> + {NewA,_} = set_properties(Wanted, ?not_DEFAULT_ADMINPROPERTIES, channelAdm, + ?not_SUPPORTED_ADMINPROPERTIES, [], [], + false, false, false), + {NewA, [extract_value(NewA, ?not_MaxQueueLength), + extract_value(NewA, ?not_MaxConsumers), + extract_value(NewA, ?not_MaxSuppliers)]}. + +set_adm(Wanted, Current) -> + {NewA,_} = set_properties(Wanted, Current, channelAdm, + ?not_SUPPORTED_ADMINPROPERTIES, + [], [], false, false, false), + {NewA, [extract_value(NewA, ?not_MaxQueueLength), + extract_value(NewA, ?not_MaxConsumers), + extract_value(NewA, ?not_MaxSuppliers)]}. + +'MaxQueueLength'(Req,channelAdm,_, _, _, _) -> admin_ok(Req). +'MaxConsumers'(Req,channelAdm,_, _, _, _)-> admin_ok(Req). +'MaxSuppliers'(Req,channelAdm,_, _, _, _)-> admin_ok(Req). + +admin_ok(Req) -> + case any:get_value(Req#'CosNotification_Property'.value) of + Val when is_integer(Val) andalso Val >= 0 -> + {ok, Req}; + _ -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + + +%%------------------------------------------------------------ +%%--------------- QOS FUNCTIONS ------------------------------ +%%------------------------------------------------------------ +%%------------------------------------------------------------ +%% function : init_qos +%% Arguments: Wanted - requested QoS to be set. +%% Returns : see set_properties/9 +%% Effect : may only be used when creating a channel!!!!!!!! +%%------------------------------------------------------------ +init_qos(Wanted) -> + LQS = set_local_qos(?not_DEFAULT_QOS, ?not_CreateInitQoS()), + set_properties(Wanted, ?not_DEFAULT_QOS, channel, ?not_SUPPORTED_QOS, + [], [], false, [], LQS). + +%%------------------------------------------------------------ +%% function : set_qos/5 +%% Arguments: Wanted - requested QoS to be set. +%% Current - current QoS OMG style +%% LQS - local representation of QoS. +%% Type - channel | admin | proxy +%% Parent - Factory if Channel, Channel if Admin etc +%% Childs - Admins if Channel etc +%% Returns : see set_properties/9 +%%------------------------------------------------------------ +set_qos(Wanted, {Current, LQS}, proxy, Parent, _) -> + set_properties(Wanted, Current, proxy, ?not_SUPPORTED_QOS, [], [], Parent, false,LQS); +set_qos(Wanted, {Current, LQS}, admin, Parent, Childs) -> + set_properties(Wanted, Current, admin, ?not_SUPPORTED_QOS, [], [], Parent, Childs,LQS); +set_qos(Wanted, {Current, LQS}, channel, _, Childs) -> + set_properties(Wanted, Current, channel, ?not_SUPPORTED_QOS, [], [], false, Childs,LQS). + +%%------------------------------------------------------------ +%% function : +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : ok - if requested equal to current value. +%% {ok, Req, LQS} - if new and allowed QoS +%% {unsupported,#'CosNotification_PropertyError'{}} otherwise. +%% Effect : +%%------------------------------------------------------------ +'EventReliability'(Req,channel, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetConnectionReliability(LQS), ?not_BestEffort, ?not_Persistent} of + {Val, Val, _, _} -> + %% Is the value requested. + ok; + {Val, _, Val, _} -> + {ok, Req, LQS}; + {Val, _, _, Val} -> + {ok, Req, LQS}; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end; +'EventReliability'(Req,_,_,_,_,_) -> + %% only valid to set this QoS for channels (or per-event). + {unsupported, + #'CosNotification_PropertyError'{ + code = 'UNAVAILABLE_PROPERTY', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + }. + +%%------------------------------------------------------------ +%% function : 'ConnectionReliability'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Exception: +%% Effect : +%%------------------------------------------------------------ +%% The most complex QoS to set is ConnectionReliability, and the reason for this +%% is that we cannot set the Channel to offer best effort while its children +%% offer persistent. A child may only offer Persistent if its parent do, which +%% is why we must check the following: +%% +%% # Persistent Change to Best Effort +%% _____ +%% | | (1) -> Check if children BE +%% |Chann| (2) ok <- +%% ----- +%% | +%% _____ +%% | | (3) -> Check if children BE +%% |Admin| (4) Check if parent Pers. <- +%% ----- +%% | +%% _____ +%% | | (5) -> ok +%% |Proxy| (6) Check if parent Pers. <- +%% ----- +%% NOTE: a parent always exists but we may change the QoS before creating any +%% childrens. The cases (2) and (5) is always ok, i.e., no need to confirm +%% with parent or children. +%%------------------------------------------------------------ +'ConnectionReliability'(Req, channel, _Curr, _Parent, Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetConnectionReliability(LQS), ?not_BestEffort, ?not_Persistent} of + {Val, Val, _, _} -> + %% Is the value requested. + ok; + {Val, P, Val, P} -> + %% Requested is BestEffort, Current Persistent => (1) + check_with_relatives(Childs, Req, LQS); + {Val, B, B, Val} -> + %% Requested is Persistent, Current BestEffort => (2) + {ok, Req, LQS}; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end; +'ConnectionReliability'(Req, admin, _Curr, Parent, Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetConnectionReliability(LQS), ?not_BestEffort, ?not_Persistent} of + {Val, Val, _, _} -> + %% Is the value requested. + ok; + {Val, P, Val, P} -> + %% Requested is BestEffort, Current Persistent => (3) + check_with_relatives(Childs, Req, LQS); + {Val, B, B, Val} -> + %% Requested is Persistent, Current BestEffort => (4) + check_with_relatives([Parent], Req, LQS); + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end; +'ConnectionReliability'(Req, proxy, _Curr, Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetConnectionReliability(LQS), ?not_BestEffort, ?not_Persistent} of + {Val, Val, _, _} -> + %% Is the value requested. + ok; + {Val, P, Val, P} -> + %% Requested is BestEffort, Current Persistent => (5) + {ok, Req, LQS}; + {Val, B, B, Val} -> + %% Requested is Persistent, Current BestEffort => (6) + check_with_relatives([Parent], Req, LQS); + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : 'Priority'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'Priority'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetPriority(LQS), ?not_HighestPriority, ?not_LowestPriority} of + {Val, Val, _, _} -> + ok; + {Val, _, H, L} when Val =< H, Val >= L -> + {ok, Req, LQS}; + {_, _, H, L} -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), L), + high_val=any:create(orber_tc:short(), H) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : 'StartTimeSupported'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'StartTimeSupported'(Req, _Type, _Curr, _, _, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetStartTimeSupported(LQS)} of + {Val, Val} -> + ok; + {Val, _} when Val =/= true, Val =/= false -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:boolean(), false), + high_val=any:create(orber_tc:boolean(), true) + } + } + }; + _-> + {ok, Req, LQS} + end. + +%%------------------------------------------------------------ +%% function : 'StopTimeSupported'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'StopTimeSupported'(Req, _Type, _Curr, _, _, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetStopTimeSupported(LQS)} of + {Val, Val} -> + ok; + {Val, _} when Val =/= true, Val =/= false -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:boolean(), false), + high_val=any:create(orber_tc:boolean(), true) + } + } + }; + _-> + {ok, Req, LQS} + end. + +%%------------------------------------------------------------ +%% function : 'Timeout'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'Timeout'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetTimeout(LQS)} of + {Val, Val} -> + ok; + {Val, _} when Val >= ?not_MinTimeout, Val =< ?not_MaxTimeout -> + {ok, Req, LQS}; + {Val, _} when is_integer(Val) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:unsigned_long_long(), ?not_MinTimeout), + high_val=any:create(orber_tc:unsigned_long_long(), ?not_MaxTimeout) + } + } + }; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : 'OrderPolicy'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'OrderPolicy'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetOrderPolicy(LQS), 'CosNotification':'AnyOrder'(), + 'CosNotification':'PriorityOrder'()} of + {Val, Val,_,_} -> + ok; + {Val, _, L, H} when Val >= L, Val =< H -> + {ok, Req, LQS}; + {Val, _, L, H} when is_integer(Val) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), L), + high_val=any:create(orber_tc:short(), H) + } + } + }; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + + +%%------------------------------------------------------------ +%% function : 'DiscardPolicy'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'DiscardPolicy'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetDiscardPolicy(LQS), ?not_AnyOrder, ?not_PriorityOrder} of + {Val, Val,_,_} -> + ok; + {Val, _, L, H} when Val >= L, Val =< H -> + {ok, Req, LQS}; + {Val, _, L, H} when is_integer(Val) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), L), + high_val=any:create(orber_tc:short(), H) + } + } + }; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : 'DiscardPolicy'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'MaximumBatchSize'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetMaximumBatchSize(LQS)} of + {Val, Val} -> + ok; + {Val, _} when Val >= ?not_MinBatchSize, Val =< ?not_MaxBatchSize -> + {ok, Req, LQS}; + {Val, _} when is_integer(Val) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:unsigned_long_long(), ?not_MinBatchSize), + high_val=any:create(orber_tc:unsigned_long_long(), ?not_MaxBatchSize) + } + } + }; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'UNSUPPORTED_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:long(), ?not_MinBatchSize), + high_val=any:create(orber_tc:long(), ?not_MaxBatchSize) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : 'PacingInterval'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Comment : PacingInterval is defined to be: +%% * TimeBase::UtcT (p 57, 2.5.5, OMG TC Document telecom/98-11-01) +%% * TimeBase::TimeT (p 189, appendix B, OMG TC Document telecom/98-11-01) +%% This implementation use TimeBase::TimeT, especially since +%% TimeBase::UtcT contains information which are of no importance. +%% When writing this, the OMG homepage contained no information +%% regarding this. +%%------------------------------------------------------------ +'PacingInterval'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetPacingInterval(LQS)} of + {Val, Val} -> + ok; + {Val, _} when Val >= ?not_MinPacing, Val =< ?not_MaxPacing -> + {ok, Req, LQS}; + {Val, _} when is_integer(Val) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:unsigned_long_long(), ?not_MinPacing), + high_val=any:create(orber_tc:unsigned_long_long(), ?not_MaxPacing) + } + } + }; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_TYPE', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : 'MaxEventsPerConsumer'/6 +%% Arguments: Req - Requested QoS, #'CosNotification_Property'{} +%% Type - Requestee, channel | admin | proxy +%% Curr - Current QoS, #'CosNotification_Property'{} +%% Parent - false | ObjRef +%% Childs - false | [ObjRef1, .., ObjRefN] +%% LQS - #qos{} defined in CosNotification_Definitions.hrl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +'MaxEventsPerConsumer'(Req, _Type, _Curr, _Parent, _Childs, LQS) -> + case {any:get_value(Req#'CosNotification_Property'.value), + ?not_GetMaxEventsPerConsumer(LQS)} of + {Val, Val} -> + ok; + {Val, _} when is_integer(Val) andalso + Val >= ?not_MinConsumerEvents andalso + Val =< ?not_MaxConsumerEvents -> + {ok, Req, LQS}; + {Val, _} when is_integer(Val) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:unsigned_long_long(), ?not_MinConsumerEvents), + high_val=any:create(orber_tc:unsigned_long_long(), ?not_MaxConsumerEvents) + } + } + }; + _-> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'UNSUPPORTED_VALUE', + name = Req#'CosNotification_Property'.name, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:long(), ?not_MinConsumerEvents), + high_val=any:create(orber_tc:long(), ?not_MaxConsumerEvents) + } + } + } + end. + +%%------------------------------------------------------------ +%% function : validate_qos/5 +%% Arguments: Wanted - requested QoS to be set. +%% Curr - current QoS OMG style and LQS, local +%% representation of QoS, grouped as {OMGQ, LQS} +%% Type - channel | admin | proxy +%% Parent - Factory if Channel, Channel if Admin etc +%% Childs - Admins if Channel etc +%% Returns : NamedPropertySeq | #'CosNotification_UnsupportedQoS'{} +%% case 1 if all supported, case 2 if at least 1 QoS not +%% supported. +%% See also p59, 2.5.6.4, OMG TC Document telecom/98-11-01. Quote: +%% "If the supplied QoS is supported, it returns additional QoS +%% properties which could be optionally added as well." +%%------------------------------------------------------------ +validate_qos(Wanted, Curr, Type, Parent, Childs) -> + %% If not supported this function will raise an exception, which we should + %% not catch, but all we need to is to raise the exception as it is. + {_, LQS}=set_qos(Wanted, Curr, Type, Parent, Childs), + NewNPR = check_limits(LQS, ?not_QOS_LIMITS), + remove_qos(Wanted, LQS, NewNPR). + +remove_qos([], _, NPR) -> + NPR; +remove_qos([H|T], LQS, NPR) -> + NewNPR=remove(NPR, H#'CosNotification_Property'.name), + remove_qos(T, LQS, NewNPR). + +check_limits(LQS, NPR) -> + case {?not_GetEventReliability(LQS), ?not_GetConnectionReliability(LQS), + ?not_Persistent, ?not_BestEffort} of + {P,P,P,_B} -> + New = #'CosNotification_NamedPropertyRange' + {name=?not_EventReliability, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_BestEffort), + high_val=any:create(orber_tc:short(), ?not_BestEffort) + }}, + NewNPR=change(NPR, ?not_EventReliability, New), + remove(NewNPR, ?not_ConnectionReliability); + {_,B,_P,B} -> + remove(NPR, ?not_EventReliability); + {B,P,P,B} -> + New = #'CosNotification_NamedPropertyRange' + {name=?not_ConnectionReliability, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_BestEffort), + high_val=any:create(orber_tc:short(), ?not_BestEffort) + }}, + change(NPR, ?not_ConnectionReliability, New) + end. + +%%------------------------------------------------------------ +%% function : validate_event_qos/2 +%% Arguments: Wanted - requested QoS to be set. +%% Curr - LQS, local representation of QoS +%% Returns : NamedPropertySeq | #'CosNotification_UnsupportedQoS'{} +%% case 1 if all supported, case 2 if at least 1 QoS not +%% supported. +%%------------------------------------------------------------ +validate_event_qos(Wanted, Curr) -> + v_e_q_helper(Wanted, Curr, []), + []. +v_e_q_helper([], _Curr, []) -> + %% Parsed all and foynd no conflicts. + ok; +v_e_q_helper([], _Curr, Unsupp) -> + %% Not possible to use these requested QoS. + corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupp}); + +%%--- EventReliability ---%% +v_e_q_helper([#'CosNotification_Property'{name=?not_EventReliability, + value=#any{value=?not_BestEffort}}|T], Curr, Unsupp) -> + %% Always ok. + v_e_q_helper(T, Curr, Unsupp); +v_e_q_helper([#'CosNotification_Property'{name=?not_EventReliability, + value=#any{value=?not_Persistent}}|T], Curr, Unsupp) + when ?not_GetConnectionReliability(Curr) =/= ?not_BestEffort, + ?not_GetEventReliability(Curr) =/= ?not_BestEffort, + ?not_GetStopTimeSupported(Curr) =/= true -> + v_e_q_helper(T, Curr, Unsupp); +v_e_q_helper([#'CosNotification_Property'{name=?not_EventReliability}|T], + Curr, Unsupp) -> + %% Impossible to set to Persistent if the connection reliability is best effort. + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'UNAVAILABLE_VALUE', name = ?not_EventReliability, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null)}}|Unsupp]); + +%%--- Priority ---%% +v_e_q_helper([#'CosNotification_Property'{name=?not_Priority, value=#any{value=V}}|T], Curr, + Unsupp) -> + if + ?not_GetOrderPolicy(Curr) =/= ?not_AnyOrder, + ?not_GetOrderPolicy(Curr) =/= ?not_Priority, + ?not_GetDiscardPolicy(Curr) =/= ?not_Priority -> + %% No use setting Priority since it's not currently used. + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'UNAVAILABLE_VALUE', name = ?not_Priority, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + }}|Unsupp]); + V =< ?not_HighestPriority, V >= ?not_LowestPriority -> + v_e_q_helper(T, Curr, Unsupp); + true -> + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'BAD_VALUE', name = ?not_Priority, + available_range = + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), + ?not_LowestPriority), + high_val=any:create(orber_tc:short(), + ?not_HighestPriority)}}|Unsupp]) + end; + +%%--- StartTime ---%% +v_e_q_helper([#'CosNotification_Property'{name=?not_StartTime}|T], Curr, Unsupp) + when ?not_GetStartTimeSupported(Curr) =/= false, + ?not_GetEventReliability(Curr) =/= ?not_Persistent -> + v_e_q_helper(T, Curr, Unsupp); +v_e_q_helper([#'CosNotification_Property'{name=?not_StartTime}|T], Curr, Unsupp) -> + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'UNAVAILABLE_VALUE', name = ?not_StartTime, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + }}|Unsupp]); + +%%--- StopTime ---%% +v_e_q_helper([#'CosNotification_Property'{name=?not_StopTime}|T], Curr, Unsupp) + when ?not_GetStopTimeSupported(Curr) =/= false, + ?not_GetEventReliability(Curr) =/= ?not_Persistent -> + v_e_q_helper(T, Curr, Unsupp); +v_e_q_helper([#'CosNotification_Property'{name=?not_StopTime}|T], Curr, Unsupp) -> + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'UNAVAILABLE_VALUE', name = ?not_StopTime, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + }}|Unsupp]); + +%%--- Timeout ---%% +v_e_q_helper([#'CosNotification_Property'{name=?not_Timeout}|T], Curr, Unsupp) + when ?not_GetStopTimeSupported(Curr) =/= false, + ?not_GetEventReliability(Curr) =/= ?not_Persistent -> + v_e_q_helper(T, Curr, Unsupp); +v_e_q_helper([#'CosNotification_Property'{name=?not_Timeout}|T], Curr, Unsupp) -> + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'UNAVAILABLE_VALUE', name = ?not_Timeout, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + }}|Unsupp]); + +%%--- Unknown Event QoS ---%% +v_e_q_helper([#'CosNotification_Property'{name=Name}|T], Curr, Unsupp) -> + %% Unsupported property. + v_e_q_helper(T, Curr, [#'CosNotification_PropertyError' + {code = 'BAD_PROPERTY', name = Name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + }}|Unsupp]); +v_e_q_helper(What, _, _) -> + %% Not a Property struct. + orber:dbg("[~p] CosNotification_Common:v_e_q_helper(~p);~n" + "Not a CosNotification_Property struct.", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%-------------- QOS HELP FUNCTIONS -------------------------- +%%------------------------------------------------------------ +%% function : set_properties/9 +%% Arguments: Wanted - requested QoS to be set. +%% Current - current QoS OMG style +%% Type - channel | admin | proxy +%% Supported - List of supported QoS +%% Unsupp - acc +%% NewQoS - acc +%% Parent - Factory if Channel, Channel if Admin etc +%% Childs - Admins if Channel etc +%% LQS - local representation of QoS. +%% Returns : {NewOMGStyleQoS, NewLocalQoS} | #'CosNotification_UnsupportedQoS'{} +%%------------------------------------------------------------ +set_properties([], Curr, channelAdm, _, [], NewQoS,_,_,LAS) -> + merge_properties(NewQoS, Curr, LAS); +set_properties([], Curr, _, _, [], NewQoS,_,_,LQS) -> + %% set_local_qos and merge_properties are help functions found at the end of QoS + %% functions. + NewLQS = set_local_qos(NewQoS, LQS), + merge_properties(NewQoS, Curr, NewLQS); +set_properties([], _, channelAdm, _, Unsupp, _,_,_,_) -> + corba:raise(#'CosNotification_UnsupportedAdmin'{admin_err = Unsupp}); +set_properties([], _, _, _, Unsupp, _,_,_,_) -> + corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupp}); + +set_properties([Req|Tail], Curr, Type, Supported, Unsupp, NewQoS, Parent, Childs,LQS) -> + %% set_values and is_supported are help functions found at the end of QoS + %% functions. + case set_values(is_supported(Supported, Req), Req, Type, Curr, Parent, Childs,LQS) of + {unsupported, U} -> + set_properties(Tail, Curr, Type, Supported, [U|Unsupp], NewQoS, Parent, Childs,LQS); + {ok, S, NewLQS} -> + set_properties(Tail, Curr, Type, Supported, Unsupp, [S|NewQoS], Parent, Childs,NewLQS); + {ok, S} -> + set_properties(Tail, Curr, Type, Supported, Unsupp, [S|NewQoS], Parent, Childs,LQS); + ok -> + set_properties(Tail, Curr, Type, Supported, Unsupp, NewQoS, Parent, Childs,LQS) + end. + + +set_values(unsupported,Req,_,_,_,_,_) -> + {unsupported, + #'CosNotification_PropertyError'{ + code = 'BAD_PROPERTY', + name = Req#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + }; +set_values({ok, Func}, Req, Type, Curr, Parent, Childs, LQS) -> + ?MODULE:Func(Req, Type, Curr, Parent, Childs, LQS). + +%% Update OMG style QoS list with new values. +merge_properties([], NewCurrQoS, LQS) -> + {NewCurrQoS, LQS}; +merge_properties([H|T], Curr, LQS) -> + merge_properties(T, lists:keyreplace(H#'CosNotification_Property'.name, %% get key. + #'CosNotification_Property'.name, %% get index. + Curr, H), LQS). + +%% Is the Property S among our supported QoS? +is_supported([], _) -> + unsupported; +is_supported([{Name, Func}|_], #'CosNotification_Property'{name=Name}) -> + {ok, Func}; +is_supported([_|T], S) -> + is_supported(T, S). + +%% Find matching S-Property from a list of OMG style QoS +extract([], _) -> unsupported; +extract([H|_T], S) when H#'CosNotification_Property'.name== + S#'CosNotification_Property'.name -> + {ok, H}; +extract([_|T], S) -> extract(T,S). + +%% Find matching Property name from a list of OMG style QoS +extract_value([], _) -> unsupported; +extract_value([H|_T], Key) when H#'CosNotification_Property'.name== Key -> + {ok, any:get_value(H#'CosNotification_Property'.value)}; +extract_value([_|T], Key) -> extract(T,Key). + +%% Remove matching S-QoS from a list of OMG style QoS +remove(List, Key) -> + lists:keydelete(Key, + #'CosNotification_NamedPropertyRange'.name, %% get index. + List). + +change(List, Key, New) -> + lists:keyreplace(Key, + #'CosNotification_NamedPropertyRange'.name, %% get index. + List, New). +%% Get QoS from supplied objects and check if it's the same as S. +check_with_relatives([], S, LQS) -> + {ok, S, LQS}; +check_with_relatives([undefined|T], S, LQS) -> + check_with_relatives(T, S, LQS); +check_with_relatives([H|T], S, LQS) -> + case catch extract('CosNotification_QoSAdmin':get_qos(H), S) of + {ok, S} -> + check_with_relatives(T, S, LQS); + _-> + %% Varioues reasons for this case (Object not responding, not supported) + {unsupported, + #'CosNotification_PropertyError'{ + code = 'UNAVAILABLE_PROPERTY', + name = S#'CosNotification_Property'.name, + available_range = #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:null(), null), + high_val=any:create(orber_tc:null(), null) + } + } + } + end. + +%% Set new values to locally defined representation of QoS. Using this approach is +%% necessary since we must state the record-field at compile-time. +set_local_qos([], LQS) -> LQS; +set_local_qos([#'CosNotification_Property'{name=N,value=V}|T], LQS) -> + NewLQS = + case N of + "EventReliability" -> + ?not_SetEventReliability(LQS, any:get_value(V)); + "ConnectionReliability" -> + ?not_SetConnectionReliability(LQS, any:get_value(V)); + "Priority" -> + ?not_SetPriority(LQS, any:get_value(V)); + "Timeout" -> + ?not_SetTimeout(LQS, any:get_value(V)); + "OrderPolicy" -> + ?not_SetOrderPolicy(LQS, any:get_value(V)); + "DiscardPolicy" -> + ?not_SetDiscardPolicy(LQS, any:get_value(V)); + "MaximumBatchSize" -> + ?not_SetMaximumBatchSize(LQS, any:get_value(V)); + "PacingInterval" -> + ?not_SetPacingInterval(LQS, any:get_value(V)); + "StartTimeSupported" -> + ?not_SetStartTimeSupported(LQS, any:get_value(V)); + "StopTimeSupported" -> + ?not_SetStopTimeSupported(LQS, any:get_value(V)); + "MaxEventsPerConsumer" -> + ?not_SetMaxEventsPerConsumer(LQS, any:get_value(V)) + end, + set_local_qos(T, NewLQS). + +%%%%%%%%%%%%%%%%% END QOS FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%% + +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotification_Definitions.hrl b/lib/cosNotification/src/CosNotification_Definitions.hrl new file mode 100644 index 0000000000..755b07cd5d --- /dev/null +++ b/lib/cosNotification/src/CosNotification_Definitions.hrl @@ -0,0 +1,340 @@ +%%---------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : CosNotification_Definitions.hrl +%% Purpose : +%%---------------------------------------------------------------------- + +-ifndef(COSNOTIFICATION_DEFINITIONS_HRL). +-define(COSNOTIFICATION_DEFINITIONS_HRL, true). + +%% ---------------- General comment ------------------------------------ +%% ******* README ******** +%% The prefix 'not' is short for notification, and is used to separate locally +%% defined macros from the global ones, i.e., do NOT confuse this with a negation!! +%% +%% In this file you find globally used data structures, constants etc. +%% + +%%--------------- INCLUDES --------------------------------------------- + +%%-------- Constants ------------------------------------------------- +-define(not_SupportedGrammars, ["EXTENDED_TCL"]). + +%% !!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +%% +%% If OMG redefines the values for the constants the definitions +%% below must be redefined!! +%% +%% !!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +-define(not_BestEffort, 0). +-define(not_Persistent, 1). +-define(not_EventReliability, "EventReliability"). +-define(not_ConnectionReliability, "ConnectionReliability"). +-define(not_Priority, "Priority"). +-define(not_LowestPriority, -32767). +-define(not_HighestPriority, 32767). +-define(not_DefaultPriority, 0). +-define(not_StartTime, "StartTime"). +-define(not_StopTime, "StopTime"). +-define(not_Timeout, "Timeout"). +-define(not_OrderPolicy, "OrderPolicy"). +-define(not_AnyOrder, 0). +-define(not_FifoOrder, 1). +-define(not_PriorityOrder, 2). +-define(not_DeadlineOrder, 3). +-define(not_DiscardPolicy, "DiscardPolicy"). +-define(not_LifoOrder, 4). +-define(not_RejectNewEvents, 5). +-define(not_MaximumBatchSize, "MaximumBatchSize"). +-define(not_PacingInterval, "PacingInterval"). +-define(not_StartTimeSupported, "StartTimeSupported"). +-define(not_StopTimeSupported, "StopTimeSupported"). +-define(not_MaxEventsPerConsumer, "MaxEventsPerConsumer"). +-define(not_MaxQueueLength, "MaxQueueLength"). +-define(not_MaxConsumers, "MaxConsumers"). +-define(not_MaxSuppliers, "MaxSuppliers"). + +%%--------------- QOS DEFINITIONS ---------------------------- +%% Limits for QoS. These are our own limits. +-define(not_MaxBatchSize, 10000). +-define(not_MinBatchSize, 1). +-define(not_MinTimeout, 0). +-define(not_MaxTimeout, 100000000000). +-define(not_MinPacing, 0). +-define(not_MaxPacing, 100000000000). +-define(not_MinConsumerEvents, 1). +-define(not_MaxConsumerEvents, 10000). + +-define(not_QOS_LIMITS, +[#'CosNotification_NamedPropertyRange' + {name=?not_EventReliability, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_BestEffort), + high_val=any:create(orber_tc:short(), ?not_Persistent) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_ConnectionReliability, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_BestEffort), + high_val=any:create(orber_tc:short(), ?not_Persistent) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_Priority, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_LowestPriority), + high_val=any:create(orber_tc:short(), ?not_HighestPriority) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_StartTimeSupported, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:boolean(), false), + high_val=any:create(orber_tc:boolean(), true) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_StopTimeSupported, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:boolean(), false), + high_val=any:create(orber_tc:boolean(), true) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_Timeout, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:unsigned_long_long(), ?not_MinTimeout), + high_val=any:create(orber_tc:unsigned_long_long(), ?not_MaxTimeout) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_OrderPolicy, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_AnyOrder), + high_val=any:create(orber_tc:short(), ?not_PriorityOrder) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_DiscardPolicy, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:short(), ?not_AnyOrder), + high_val=any:create(orber_tc:short(), ?not_PriorityOrder) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_MaximumBatchSize, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:long(), ?not_MinBatchSize), + high_val=any:create(orber_tc:long(), ?not_MaxBatchSize) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_PacingInterval, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:unsigned_long_long(), ?not_MinPacing), + high_val=any:create(orber_tc:unsigned_long_long(), ?not_MaxPacing) + }}, + #'CosNotification_NamedPropertyRange' + {name=?not_MaxEventsPerConsumer, + range= + #'CosNotification_PropertyRange'{ + low_val=any:create(orber_tc:long(), ?not_MinConsumerEvents), + high_val=any:create(orber_tc:long(), ?not_MaxConsumerEvents) + }} +]. + + + +%% Local record used internally, and the reason for this is we get faster +%% access to QoS settings. +-record(qos, {'EventReliability', + 'ConnectionReliability', + 'Priority', + 'StartTimeSupported', + 'StopTimeSupported', + 'Timeout', + 'OrderPolicy', + 'DiscardPolicy', + 'MaximumBatchSize', + 'PacingInterval', + 'MaxEventsPerConsumer'}). + +%% Global (OMG) representation of QoS. +-define(not_DEFAULT_QOS, +[#'CosNotification_Property'{name=?not_MaximumBatchSize, + value=any:create(orber_tc:long(), 1)}, + #'CosNotification_Property'{name=?not_PacingInterval, + value=any:create(orber_tc:unsigned_long_long(), 0)}, + #'CosNotification_Property'{name=?not_Timeout, + value=any:create(orber_tc:unsigned_long_long(), 0)}, + #'CosNotification_Property'{name=?not_MaxEventsPerConsumer, + value=any:create(orber_tc:long(), 100)}, + #'CosNotification_Property'{name=?not_OrderPolicy, + value=any:create(orber_tc:short(), + ?not_PriorityOrder)}, + #'CosNotification_Property'{name=?not_EventReliability, + value=any:create(orber_tc:short(), + ?not_BestEffort)}, + #'CosNotification_Property'{name=?not_ConnectionReliability, + value=any:create(orber_tc:short(), + ?not_BestEffort)}, + #'CosNotification_Property'{name=?not_DiscardPolicy, + value=any:create(orber_tc:short(), + ?not_RejectNewEvents)}, + #'CosNotification_Property'{name=?not_StartTimeSupported, + value=any:create(orber_tc:boolean(), false)}, + #'CosNotification_Property'{name=?not_StopTimeSupported, + value=any:create(orber_tc:boolean(), false)}, + #'CosNotification_Property'{name=?not_Priority, + value=any:create(orber_tc:short(), ?not_DefaultPriority)}]). + +%%--------------- QOS CREATORS ------------------------------- +-define(not_CreateInitQoS(), #qos{}). + +%%--------------- QOS DESTRUCTORS ---------------------------- +-define(not_DestroyQoS(Q), ok). + +%%--------------- QOS SELECTORS ------------------------------ +-define(not_GetEventReliability(Q), Q#qos.'EventReliability'). +-define(not_GetConnectionReliability(Q), Q#qos.'ConnectionReliability'). +-define(not_GetPriority(Q), Q#qos.'Priority'). +-define(not_GetStartTimeSupported(Q), Q#qos.'StartTimeSupported'). +-define(not_GetStopTimeSupported(Q), Q#qos.'StopTimeSupported'). +-define(not_GetTimeout(Q), Q#qos.'Timeout'). +-define(not_GetOrderPolicy(Q), Q#qos.'OrderPolicy'). +-define(not_GetDiscardPolicy(Q), Q#qos.'DiscardPolicy'). +-define(not_GetMaximumBatchSize(Q), Q#qos.'MaximumBatchSize'). +-define(not_GetPacingInterval(Q), Q#qos.'PacingInterval'). +-define(not_GetMaxEventsPerConsumer(Q), Q#qos.'MaxEventsPerConsumer'). + +%%--------------- QOS MODIFIERS ------------------------------ +-define(not_SetEventReliability(Q,D), Q#qos{'EventReliability'=D}). +-define(not_SetConnectionReliability(Q,D), Q#qos{'ConnectionReliability'=D}). +-define(not_SetPriority(Q,D), Q#qos{'Priority'=D}). +-define(not_SetStartTimeSupported(Q,D), Q#qos{'StartTimeSupported'=D}). +-define(not_SetStopTimeSupported(Q,D), Q#qos{'StopTimeSupported'=D}). +-define(not_SetTimeout(Q,D), Q#qos{'Timeout'=D}). +-define(not_SetOrderPolicy(Q,D), Q#qos{'OrderPolicy'=D}). +-define(not_SetDiscardPolicy(Q,D), Q#qos{'DiscardPolicy'=D}). +-define(not_SetMaximumBatchSize(Q,D), Q#qos{'MaximumBatchSize'=D}). +-define(not_SetPacingInterval(Q,D), Q#qos{'PacingInterval'=D}). +-define(not_SetMaxEventsPerConsumer(Q,D), Q#qos{'MaxEventsPerConsumer'=D}). + +%%--------------- StructuredEvent CREATORS ------------------- +-define(not_CreateSE(StrD,StrT,StrE,PSeqV,PSeqF,AnyR), +#'CosNotification_StructuredEvent'{header = + #'CosNotification_EventHeader'{fixed_header = + #'CosNotification_FixedEventHeader'{event_type = + #'CosNotification_EventType'{domain_name=StrD, + type_name=StrT}, + event_name = StrE}, + variable_header = PSeqV}, + filterable_data = PSeqF, + remainder_of_body = AnyR}). +%% Can be used in guards. +-define(not_isConvertedAny(E), + (((E#'CosNotification_StructuredEvent'.header) + #'CosNotification_EventHeader'.fixed_header) + #'CosNotification_FixedEventHeader'.event_type) + #'CosNotification_EventType'.type_name == "%ANY"). +%% Can NOT be used in guards!!!!! +-define(not_isConvertedStructured(E), + any:get_typecode(E) == 'CosNotification_StructuredEvent':tc()). + +%%--------------- StructuredEvent DESTRUCTORS ---------------- +-define(not_DestroySE(E), ok). + +%%--------------- StructuredEvent SELECTORS ------------------ +-define(not_GetSEHeader(E), E#'StructuredEvent'.header). +-define(not_GetSEFixedHeader(E), E#'StructuredEvent'.header). + +%%--------------- StructuredEvent MODIFIERS ------------------ + +%%-------- QoS support ----------------------------------------------- +-define(not_SUPPORTED_QOS, +[{?not_EventReliability, 'EventReliability'}, + {?not_ConnectionReliability, 'ConnectionReliability'}, + {?not_Priority, 'Priority'}, + {?not_StartTimeSupported, 'StartTimeSupported'}, + {?not_StopTimeSupported, 'StopTimeSupported'}, + {?not_Timeout, 'Timeout'}, + {?not_OrderPolicy, 'OrderPolicy'}, + {?not_DiscardPolicy, 'DiscardPolicy'}, + {?not_MaximumBatchSize, 'MaximumBatchSize'}, + {?not_PacingInterval, 'PacingInterval'}, + {?not_MaxEventsPerConsumer, 'MaxEventsPerConsumer'}]). + +%%-------- ADMINPROPERTIESADMIN -------------------------------------- + +%% According to the OMG TC Document telecom/98-11-01, p 63 (section 2.5.7), the +%% default-value for these 3 admin properties is zero, which means that no limit +%% applies to that property. +-define(not_DEFAULT_ADMINPROPERTIES, +[#'CosNotification_Property'{name=?not_MaxQueueLength, + value=any:create(orber_tc:long(), 0)}, + #'CosNotification_Property'{name=?not_MaxConsumers, + value=any:create(orber_tc:long(), 0)}, + #'CosNotification_Property'{name=?not_MaxSuppliers, + value=any:create(orber_tc:long(), 0)}]). + +-define(not_SUPPORTED_ADMINPROPERTIES, +[{?not_MaxQueueLength, 'MaxQueueLength'}, + {?not_MaxConsumers, 'MaxConsumers'}, + {?not_MaxSuppliers, 'MaxSuppliers'}]). + + +%%-------- MISC -------------------------------------------------------- + +-define(not_DEFAULT_SETTINGS, [{pullInterval, 20}, + {filterOp, 'OR_OP'}, + {gcTime, 60}, + {gcLimit, 50}, + {timeService, undefined}, + {typecheck, true}, + {tty, false}, + {logfile, false}, + {server_options, []}]). +-define(not_CreateDBKey, term_to_binary({now(), node()})). + +-define(DEBUG_LEVEL, 3). + +-ifdef(debug). + +-define(debug_print(F,A), io:format("[~p(~p)] "++F,[?MODULE, ?LINE]++A)). +-define(DBG(F,A), io:format("[~p(~p)] "++F,[?MODULE, ?LINE]++A)). +-define(not_TypeCheck(O,I), ok). +%-define(not_TypeCheck(O,M), 'CosNotification_Common':type_check(O,M)). + +-else. + +-define(debug_print(F,A), ok). +-define(DBG(F,A), ok). +-define(not_TypeCheck(O,I), ok). + +-endif. + + + +-endif. +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotifyChannelAdmin.cfg b/lib/cosNotification/src/CosNotifyChannelAdmin.cfg new file mode 100644 index 0000000000..8647b281a2 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyChannelAdmin.cfg @@ -0,0 +1,60 @@ +{this, "CosNotifyChannelAdmin::EventChannel"}. +{from, "CosNotifyChannelAdmin::EventChannel"}. +{{handle_info, "CosNotifyChannelAdmin::EventChannel"}, true}. +{this, "CosNotifyChannelAdmin::EventChannelFactory"}. +{from, "CosNotifyChannelAdmin::EventChannelFactory"}. +{{handle_info, "CosNotifyChannelAdmin::EventChannelFactory"}, true}. +{this, "CosNotifyChannelAdmin::SupplierAdmin"}. +{from, "CosNotifyChannelAdmin::SupplierAdmin"}. +{{handle_info, "CosNotifyChannelAdmin::SupplierAdmin"}, true}. +{this, "CosNotifyChannelAdmin::ConsumerAdmin"}. +{from, "CosNotifyChannelAdmin::ConsumerAdmin"}. +{{handle_info, "CosNotifyChannelAdmin::ConsumerAdmin"}, true}. +{this, "CosNotifyChannelAdmin::StructuredProxyPushSupplier"}. +{from, "CosNotifyChannelAdmin::StructuredProxyPushSupplier"}. +{{handle_info, "CosNotifyChannelAdmin::StructuredProxyPushSupplier"}, true}. +{{impl, "CosNotifyChannelAdmin::StructuredProxyPushSupplier"}, "PusherSupplier_impl"}. +{this, "CosNotifyChannelAdmin::StructuredProxyPullSupplier"}. +{from, "CosNotifyChannelAdmin::StructuredProxyPullSupplier"}. +{{handle_info, "CosNotifyChannelAdmin::StructuredProxyPullSupplier"}, true}. +{{impl, "CosNotifyChannelAdmin::StructuredProxyPullSupplier"}, "PullerSupplier_impl"}. +{this, "CosNotifyChannelAdmin::ProxyPushSupplier"}. +{from, "CosNotifyChannelAdmin::ProxyPushSupplier"}. +{{handle_info, "CosNotifyChannelAdmin::ProxyPushSupplier"}, true}. +{{impl, "CosNotifyChannelAdmin::ProxyPushSupplier"}, "PusherSupplier_impl"}. +{this, "CosNotifyChannelAdmin::ProxyPullSupplier"}. +{from, "CosNotifyChannelAdmin::ProxyPullSupplier"}. +{{handle_info, "CosNotifyChannelAdmin::ProxyPullSupplier"}, true}. +{{impl, "CosNotifyChannelAdmin::ProxyPullSupplier"}, "PullerSupplier_impl"}. +{this, "CosNotifyChannelAdmin::SequenceProxyPushSupplier"}. +{from, "CosNotifyChannelAdmin::SequenceProxyPushSupplier"}. +{{handle_info, "CosNotifyChannelAdmin::SequenceProxyPushSupplier"}, true}. +{{impl, "CosNotifyChannelAdmin::SequenceProxyPushSupplier"}, "PusherSupplier_impl"}. +{this, "CosNotifyChannelAdmin::SequenceProxyPullSupplier"}. +{from, "CosNotifyChannelAdmin::SequenceProxyPullSupplier"}. +{{handle_info, "CosNotifyChannelAdmin::SequenceProxyPullSupplier"}, true}. +{{impl, "CosNotifyChannelAdmin::SequenceProxyPullSupplier"}, "PullerSupplier_impl"}. +{this, "CosNotifyChannelAdmin::StructuredProxyPushConsumer"}. +{from, "CosNotifyChannelAdmin::StructuredProxyPushConsumer"}. +{{handle_info, "CosNotifyChannelAdmin::StructuredProxyPushConsumer"}, true}. +{{impl, "CosNotifyChannelAdmin::StructuredProxyPushConsumer"}, "PusherConsumer_impl"}. +{this, "CosNotifyChannelAdmin::StructuredProxyPullConsumer"}. +{from, "CosNotifyChannelAdmin::StructuredProxyPullConsumer"}. +{{handle_info, "CosNotifyChannelAdmin::StructuredProxyPullConsumer"}, true}. +{{impl, "CosNotifyChannelAdmin::StructuredProxyPullConsumer"}, "PullerConsumer_impl"}. +{this, "CosNotifyChannelAdmin::ProxyPushConsumer"}. +{from, "CosNotifyChannelAdmin::ProxyPushConsumer"}. +{{handle_info, "CosNotifyChannelAdmin::ProxyPushConsumer"}, true}. +{{impl, "CosNotifyChannelAdmin::ProxyPushConsumer"}, "PusherConsumer_impl"}. +{this, "CosNotifyChannelAdmin::ProxyPullConsumer"}. +{from, "CosNotifyChannelAdmin::ProxyPullConsumer"}. +{{handle_info, "CosNotifyChannelAdmin::ProxyPullConsumer"}, true}. +{{impl, "CosNotifyChannelAdmin::ProxyPullConsumer"}, "PullerConsumer_impl"}. +{this, "CosNotifyChannelAdmin::SequenceProxyPushConsumer"}. +{from, "CosNotifyChannelAdmin::SequenceProxyPushConsumer"}. +{{handle_info, "CosNotifyChannelAdmin::SequenceProxyPushConsumer"}, true}. +{{impl, "CosNotifyChannelAdmin::SequenceProxyPushConsumer"}, "PusherConsumer_impl"}. +{this, "CosNotifyChannelAdmin::SequenceProxyPullConsumer"}. +{from, "CosNotifyChannelAdmin::SequenceProxyPullConsumer"}. +{{handle_info, "CosNotifyChannelAdmin::SequenceProxyPullConsumer"}, true}. +{{impl, "CosNotifyChannelAdmin::SequenceProxyPullConsumer"}, "PullerConsumer_impl"}. diff --git a/lib/cosNotification/src/CosNotifyChannelAdmin.idl b/lib/cosNotification/src/CosNotifyChannelAdmin.idl new file mode 100644 index 0000000000..b345ff5423 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyChannelAdmin.idl @@ -0,0 +1,275 @@ +#ifndef _COS_NOTIFYCHANNELADMIN_IDL_ +#define _COS_NOTIFYCHANNELADMIN_IDL_ + +#pragma prefix "omg.org" + +#include<cosNotificationAppComm.idl> +#include<CosNotifyFilter.idl> +#include<CosNotifyComm.idl> +#include<CosNotification.idl> + +module CosNotifyChannelAdmin { + exception ConnectionAlreadyActive {}; + exception ConnectionAlreadyInactive {}; + exception NotConnected {}; + // Forward declarations + interface ConsumerAdmin; + interface SupplierAdmin; + interface EventChannel; + interface EventChannelFactory; + + enum ProxyType { + PUSH_ANY, + PULL_ANY, + PUSH_STRUCTURED, + PULL_STRUCTURED, + PUSH_SEQUENCE, + PULL_SEQUENCE}; + + enum ObtainInfoMode { + ALL_NOW_UPDATES_OFF, + ALL_NOW_UPDATES_ON, + NONE_NOW_UPDATES_OFF, + NONE_NOW_UPDATES_ON}; + + interface ProxyConsumer : CosNotification::QoSAdmin, CosNotifyFilter::FilterAdmin { + readonly attribute ProxyType MyType; + readonly attribute SupplierAdmin MyAdmin; + + CosNotification::EventTypeSeq obtain_subscription_types(in ObtainInfoMode mode); + + void validate_event_qos (in CosNotification::QoSProperties required_qos, + out CosNotification::NamedPropertyRangeSeq available_qos) + raises (CosNotification::UnsupportedQoS); + }; // ProxyConsumer + + interface ProxySupplier : CosNotification::QoSAdmin, CosNotifyFilter::FilterAdmin { + readonly attribute ProxyType MyType; + readonly attribute ConsumerAdmin MyAdmin; + attribute CosNotifyFilter::MappingFilter priority_filter; + attribute CosNotifyFilter::MappingFilter lifetime_filter; + + CosNotification::EventTypeSeq obtain_offered_types(in ObtainInfoMode mode); + + void validate_event_qos (in CosNotification::QoSProperties required_qos, + out CosNotification::NamedPropertyRangeSeq available_qos) + raises (CosNotification::UnsupportedQoS); + }; // ProxySupplier + + interface ProxyPushConsumer : ProxyConsumer, CosNotifyComm::PushConsumer, + CosEventChannelAdmin::ProxyPushConsumer { + void connect_any_push_supplier (in CosEventComm::PushSupplier push_supplier) + raises(CosEventChannelAdmin::AlreadyConnected); + }; // ProxyPushConsumer + + interface StructuredProxyPushConsumer : ProxyConsumer, CosNotifyComm::StructuredPushConsumer { + void connect_structured_push_supplier (in CosNotifyComm::StructuredPushSupplier push_supplier) + raises(CosEventChannelAdmin::AlreadyConnected); + }; // StructuredProxyPushConsumer + + interface SequenceProxyPushConsumer : ProxyConsumer, CosNotifyComm::SequencePushConsumer { + void connect_sequence_push_supplier (in CosNotifyComm::SequencePushSupplier push_supplier) + raises(CosEventChannelAdmin::AlreadyConnected); + }; // SequenceProxyPushConsumer + + interface ProxyPullSupplier : ProxySupplier, CosNotifyComm::PullSupplier, + CosEventChannelAdmin::ProxyPullSupplier, oe_CosNotificationComm::Event { + void connect_any_pull_consumer (in CosEventComm::PullConsumer pull_consumer) + raises(CosEventChannelAdmin::AlreadyConnected); + }; // ProxyPullSupplier + + interface StructuredProxyPullSupplier : ProxySupplier, CosNotifyComm::StructuredPullSupplier, + oe_CosNotificationComm::Event { + void connect_structured_pull_consumer (in CosNotifyComm::StructuredPullConsumer pull_consumer) + raises(CosEventChannelAdmin::AlreadyConnected); + }; // StructuredProxyPullSupplier + + interface SequenceProxyPullSupplier : ProxySupplier, CosNotifyComm::SequencePullSupplier, + oe_CosNotificationComm::Event { + void connect_sequence_pull_consumer (in CosNotifyComm::SequencePullConsumer pull_consumer) + raises(CosEventChannelAdmin::AlreadyConnected); + }; // SequenceProxyPullSupplier + + interface ProxyPullConsumer : ProxyConsumer, CosNotifyComm::PullConsumer, + CosEventChannelAdmin::ProxyPullConsumer { + void connect_any_pull_supplier (in CosEventComm::PullSupplier pull_supplier) + raises(CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises(ConnectionAlreadyInactive, NotConnected); + + void resume_connection() + raises(ConnectionAlreadyActive, NotConnected); + }; // ProxyPullConsumer + + interface StructuredProxyPullConsumer : ProxyConsumer, CosNotifyComm::StructuredPullConsumer { + void connect_structured_pull_supplier (in CosNotifyComm::StructuredPullSupplier pull_supplier) + raises(CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises(ConnectionAlreadyInactive, NotConnected); + + void resume_connection() + raises(ConnectionAlreadyActive, NotConnected); + }; // StructuredProxyPullConsumer + + interface SequenceProxyPullConsumer : ProxyConsumer, CosNotifyComm::SequencePullConsumer { + void connect_sequence_pull_supplier (in CosNotifyComm::SequencePullSupplier pull_supplier) + raises(CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises(ConnectionAlreadyInactive, NotConnected); + + void resume_connection() + raises(ConnectionAlreadyActive, NotConnected); + }; // SequenceProxyPullConsumer + + interface ProxyPushSupplier : ProxySupplier, CosNotifyComm::PushSupplier, + CosEventChannelAdmin::ProxyPushSupplier, oe_CosNotificationComm::Event { + void connect_any_push_consumer (in CosEventComm::PushConsumer push_consumer) + raises(CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises(ConnectionAlreadyInactive, NotConnected); + + void resume_connection() + raises(ConnectionAlreadyActive, NotConnected); + }; // ProxyPushSupplier + + interface StructuredProxyPushSupplier : ProxySupplier, CosNotifyComm::StructuredPushSupplier, + oe_CosNotificationComm::Event { + void connect_structured_push_consumer (in CosNotifyComm::StructuredPushConsumer push_consumer) + raises(CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises(ConnectionAlreadyInactive, NotConnected); + + void resume_connection() + raises(ConnectionAlreadyActive, NotConnected); + }; // StructuredProxyPushSupplier + + interface SequenceProxyPushSupplier : ProxySupplier, CosNotifyComm::SequencePushSupplier, + oe_CosNotificationComm::Event { + void connect_sequence_push_consumer (in CosNotifyComm::SequencePushConsumer push_consumer) + raises(CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises(ConnectionAlreadyInactive, NotConnected); + + void resume_connection() + raises(ConnectionAlreadyActive, NotConnected); + }; // SequenceProxyPushSupplier + + + typedef long ProxyID; + typedef sequence <ProxyID> ProxyIDSeq; + + enum ClientType { + ANY_EVENT, + STRUCTURED_EVENT, + SEQUENCE_EVENT}; + + enum InterFilterGroupOperator { + AND_OP, + OR_OP }; + + typedef long AdminID; + typedef sequence<AdminID> AdminIDSeq; + + exception AdminNotFound {}; + exception ProxyNotFound {}; + + struct AdminLimit { + CosNotification::PropertyName name; + CosNotification::PropertyValue value; + }; + + exception AdminLimitExceeded { AdminLimit admin_property_err; }; + + interface ConsumerAdmin : CosNotification::QoSAdmin, CosNotifyComm::NotifySubscribe, + CosNotifyFilter::FilterAdmin, CosEventChannelAdmin::ConsumerAdmin, + oe_CosNotificationComm::Event { + readonly attribute AdminID MyID; + readonly attribute EventChannel MyChannel; + readonly attribute InterFilterGroupOperator MyOperator; + attribute CosNotifyFilter::MappingFilter priority_filter; + attribute CosNotifyFilter::MappingFilter lifetime_filter; + readonly attribute ProxyIDSeq pull_suppliers; + readonly attribute ProxyIDSeq push_suppliers; + + ProxySupplier get_proxy_supplier (in ProxyID proxy_id) + raises (ProxyNotFound); + + ProxySupplier obtain_notification_pull_supplier (in ClientType ctype, out ProxyID proxy_id) + raises (AdminLimitExceeded); + + ProxySupplier obtain_notification_push_supplier (in ClientType ctype, out ProxyID proxy_id) + raises (AdminLimitExceeded); + + void destroy(); + }; // ConsumerAdmin + + interface SupplierAdmin : CosNotification::QoSAdmin, CosNotifyComm::NotifyPublish, + CosNotifyFilter::FilterAdmin, CosEventChannelAdmin::SupplierAdmin , + oe_CosNotificationComm::Event { + readonly attribute AdminID MyID; + readonly attribute EventChannel MyChannel; + readonly attribute InterFilterGroupOperator MyOperator; + readonly attribute ProxyIDSeq pull_consumers; + readonly attribute ProxyIDSeq push_consumers; + + ProxyConsumer get_proxy_consumer (in ProxyID proxy_id) + raises (ProxyNotFound); + + ProxyConsumer obtain_notification_pull_consumer (in ClientType ctype, out ProxyID proxy_id) + raises (AdminLimitExceeded); + + ProxyConsumer obtain_notification_push_consumer (in ClientType ctype, out ProxyID proxy_id) + raises (AdminLimitExceeded); + + void destroy(); + }; // SupplierAdmin + + interface EventChannel : CosNotification::QoSAdmin, CosNotification::AdminPropertiesAdmin, + CosEventChannelAdmin::EventChannel, + oe_CosNotificationComm::Event { + readonly attribute EventChannelFactory MyFactory; + readonly attribute ConsumerAdmin default_consumer_admin; + readonly attribute SupplierAdmin default_supplier_admin; + readonly attribute CosNotifyFilter::FilterFactory default_filter_factory; + + ConsumerAdmin new_for_consumers(in InterFilterGroupOperator op, out AdminID id); + + SupplierAdmin new_for_suppliers(in InterFilterGroupOperator op, out AdminID id); + + ConsumerAdmin get_consumeradmin (in AdminID id) + raises (AdminNotFound); + + SupplierAdmin get_supplieradmin (in AdminID id) + raises (AdminNotFound); + + AdminIDSeq get_all_consumeradmins(); + + AdminIDSeq get_all_supplieradmins(); + }; // EventChannel + + typedef long ChannelID; + typedef sequence<ChannelID> ChannelIDSeq; + exception ChannelNotFound {}; + + interface EventChannelFactory { + EventChannel create_channel (in CosNotification::QoSProperties initial_qos, + in CosNotification::AdminProperties initial_admin, + out ChannelID id) + raises(CosNotification::UnsupportedQoS, CosNotification::UnsupportedAdmin); + + ChannelIDSeq get_all_channels(); + + EventChannel get_event_channel (in ChannelID id) + raises (ChannelNotFound); + }; // EventChannelFactory +}; // CosNotifyChannelAdmin + + +#endif /* ifndef _COS_NOTIFYCHANNELADMIN_IDL_ */ + diff --git a/lib/cosNotification/src/CosNotifyChannelAdmin_ConsumerAdmin_impl.erl b/lib/cosNotification/src/CosNotifyChannelAdmin_ConsumerAdmin_impl.erl new file mode 100644 index 0000000000..5ac8c810ea --- /dev/null +++ b/lib/cosNotification/src/CosNotifyChannelAdmin_ConsumerAdmin_impl.erl @@ -0,0 +1,670 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%------------------------------------------------------------------- +%% File : CosNotifyChannelAdmin_ConsumerAdmin_impl.erl +%% Purpose : +%%------------------------------------------------------------------- + +-module('CosNotifyChannelAdmin_ConsumerAdmin_impl'). + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::ConsumerAdmin ----------------- +-export([get_proxy_supplier/4, + obtain_notification_pull_supplier/4, + obtain_notification_push_supplier/4, + destroy/3]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotifyComm::NotifySubscribe ---------- +-export([subscription_change/5]). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ------------ +-export([add_filter/4, + remove_filter/4, + get_filter/4, + get_all_filters/3, + remove_all_filters/3]). + +%%----- Inherit from CosEventChannelAdmin::ConsumerAdmin ----- +-export([obtain_push_supplier/3, + obtain_pull_supplier/3]). + +%% Attributes (external) +-export(['_get_MyID'/3, + '_get_MyChannel'/3, + '_get_MyOperator'/3, + '_get_priority_filter'/3, + '_set_priority_filter'/4, + '_get_lifetime_filter'/3, + '_set_lifetime_filter'/4, + '_get_pull_suppliers'/3, + '_get_push_suppliers'/3]). + +%%--------------- Internal ----------------------------------- +%%----- Inherit from cosNotificationComm --------------------- +-export([callAny/5, + callSeq/5]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {myId, + myChannel, + myChannelPid, + myOperator, + myFilters = [], + mySuppliers = [], + idCounter = 0, + priorityFilter, + lifetimeFilter, + etsR, + qosGlobal, + qosLocal, + options}). + +%% Data structures constructors +-define(get_InitState(_MyID, _MyCh, _MyChP, _MyOp, _PFil, _LFil, _QoS, _LQS, _O), + #state{myId = _MyID, + myChannel = _MyCh, + myChannelPid = _MyChP, + myOperator = _MyOp, + priorityFilter = _PFil, + lifetimeFilter = _LFil, + qosGlobal = _QoS, + qosLocal = _LQS, + options = _O, + etsR = ets:new(oe_ets, [set, protected])}). + +%% Data structures selectors +-define(get_PushSupplierIDs(S), find_ids(S#state.mySuppliers, pusher)). +-define(get_PullSupplierIDs(S), find_ids(S#state.mySuppliers, puller)). +-define(get_AllSuppliers(S), S#state.mySuppliers). +-define(get_AllSupplierRefs(S), find_refs(S#state.mySuppliers)). +-define(get_Supplier(S, I), find_obj(lists:keysearch(I,1,S#state.mySuppliers), + supplier)). + +-define(get_MyID(S), S#state.myId). +-define(get_MyChannel(S), S#state.myChannel). +-define(get_MyChannelPid(S), S#state.myChannelPid). +-define(get_MyOperator(S), S#state.myOperator). +-define(get_PrioFilter(S), S#state.priorityFilter). +-define(get_LifeFilter(S), S#state.lifetimeFilter). +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +-define(get_Filter(S, I), find_obj(lists:keysearch(I, 1, S#state.myFilters), + filter)). +-define(get_AllFilter(S), S#state.myFilters). +-define(get_AllFilterID(S), find_ids(S#state.myFilters)). +-define(get_Options(S), S#state.options). +-define(get_IdCounter(S), S#state.idCounter). + +%% Data structures modifiers +-define(set_PrioFilter(S,D), S#state{priorityFilter=D}). +-define(set_LifeFilter(S,D), S#state{lifetimeFilter=D}). +-define(set_LocalQoS(S,D), S#state{qosLocal=D}). +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +-define(add_PushSupplier(S,I,R,P),S#state{mySuppliers=[{I,R,P,pusher}|S#state.mySuppliers]}). +-define(add_PullSupplier(S,I,R,P),S#state{mySuppliers=[{I,R,P,puller}|S#state.mySuppliers]}). +-define(del_Supplier(S,I), S#state{mySuppliers= + lists:keydelete(I, 1, + S#state.mySuppliers)}). +-define(del_SupplierRef(S,O), S#state{mySuppliers= + lists:keydelete(O, 2, + S#state.mySuppliers)}). +-define(del_SupplierPid(S,P), S#state{mySuppliers= + lists:keydelete(P, 3, + S#state.mySuppliers)}). +-define(add_Filter(S,I,O), S#state{myFilters=[{I,O}|S#state.myFilters]}). +-define(del_Filter(S,I), S#state{myFilters= + delete_filter(lists:keydelete(I, 1, + S#state.myFilters), + S#state.myFilters)}). +-define(del_AllFilter(S), S#state{myFilters=[]}). +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id( + S#state.idCounter)). + +%% MISC +-define(is_PersistentConnection(S), + ?not_GetConnectionReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_PersistentEvent(S), + ?not_GetEventReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_ANDOP(S), S#state.myOperator == 'AND_OP'). + +%%----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%----------------------------------------------------------- + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + case Info of + {'EXIT', Pid, Reason} when ?get_MyChannelPid(State) == Pid -> + ?DBG("PARENT CHANNEL: ~p TERMINATED.~n",[Reason]), + {stop, Reason, State}; + {'EXIT', Pid, normal} -> + {noreply, ?del_SupplierPid(State, Pid)}; + _Other -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([MyId, MyChannel, MyChannelPid, MyOperator, InitQoS, LQS, Options]) -> + process_flag(trap_exit, true), + PriorityFilter = corba:create_nil_objref(), + LifeTimeFilter = corba:create_nil_objref(), + {ok, ?get_InitState(MyId, MyChannel, MyChannelPid, MyOperator, + PriorityFilter, LifeTimeFilter, InitQoS, LQS, Options)}. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_ConsumerAdmin attributes ------ +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyID' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyID'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyID(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyChannel' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyChannel'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyChannel(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyOperator' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyOperator'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyOperator(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_priority_filter' +%% Type : read and write +%% Returns : +%%----------------------------------------------------------- +'_get_priority_filter'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PrioFilter(State), State}. + +'_set_priority_filter'(_OE_THIS, _OE_FROM, State, PrioFilter) -> + {reply, ok, ?set_PrioFilter(State, PrioFilter)}. + +%%----------------------------------------------------------% +%% Attribute: '_get_lifetime_filter' +%% Type : read and write +%% Returns : +%%----------------------------------------------------------- +'_get_lifetime_filter'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_LifeFilter(State), State}. +'_set_lifetime_filter'(_OE_THIS, _OE_FROM, State, LifeFilter) -> + {reply, ok, ?set_LifeFilter(State, LifeFilter)}. + +%%----------------------------------------------------------% +%% Attribute: '_get_pull_suppliers' +%% Type : readonly +%% Returns : ProxyIDSeq +%%----------------------------------------------------------- +'_get_pull_suppliers'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PullSupplierIDs(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_push_suppliers' +%% Type : readonly +%% Returns : ProxyIDSeq +%%----------------------------------------------------------- +'_get_push_suppliers'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PushSupplierIDs(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% function : get_proxy_supplier +%% Arguments: ProxyID - unique identifier (long) +%% Returns : ObjRef | {'EXCEPTION', #'ProxyNotFound'{}} +%%----------------------------------------------------------- +get_proxy_supplier(_OE_THIS, _OE_FROM, State, Proxy_id) -> + {reply, ?get_Supplier(State, Proxy_id), State}. + +%%----------------------------------------------------------% +%% function : obtain_notification_pull_supplier +%% Arguments: Ctype - 'ANY_EVENT' | 'STRUCTURED_EVENT' | 'SEQUENCE_EVENT' +%% Returns : A Proxy of the requested type. +%%----------------------------------------------------------- +obtain_notification_pull_supplier(OE_THIS, _OE_FROM, State, Ctype) -> + %% Choose which module to use. + {Mod, Type} = + case Ctype of + 'ANY_EVENT' -> + {'CosNotifyChannelAdmin_ProxyPullSupplier', 'PULL_ANY'}; + 'STRUCTURED_EVENT' -> + {'CosNotifyChannelAdmin_StructuredProxyPullSupplier', 'PULL_STRUCTURED'}; + 'SEQUENCE_EVENT' -> + {'CosNotifyChannelAdmin_SequenceProxyPullSupplier', 'PULL_SEQUENCE'}; + _ -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:" + "obtain_notification_pull_supplier(~p);~n" + "Incorrect enumerant", + [?LINE, Ctype], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end, + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case Mod:oe_create_link([Type, OE_THIS, self(), ?get_GlobalQoS(State), + ?get_LocalQoS(State), ?get_MyChannel(State), + ?get_Options(State), ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, PrRef} -> + ProxyID = ?new_Id(State), + NewState = ?add_PullSupplier(State, ProxyID, PrRef, Pid), + {reply, {PrRef, ProxyID}, ?set_IdCounter(NewState, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:" + "obtain_notification_pull_supplier();~n" + "Unable to create: ~p/~p~n" + "Reason: ~p", [?LINE, Mod, Type, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : obtain_notification_push_supplier +%% Arguments: Ctype - 'ANY_EVENT' | 'STRUCTURED_EVENT' | 'SEQUENCE_EVENT' +%% Returns : A Proxy of the requested type. +%%----------------------------------------------------------- +obtain_notification_push_supplier(OE_THIS, _OE_FROM, State, Ctype) -> + %% Choose which module to use. + {Mod, Type} = + case Ctype of + 'ANY_EVENT' -> + {'CosNotifyChannelAdmin_ProxyPushSupplier', 'PUSH_ANY'}; + 'STRUCTURED_EVENT' -> + {'CosNotifyChannelAdmin_StructuredProxyPushSupplier', 'PUSH_STRUCTURED'}; + 'SEQUENCE_EVENT' -> + {'CosNotifyChannelAdmin_SequenceProxyPushSupplier', 'PUSH_SEQUENCE'}; + _ -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:" + "obtain_notification_push_supplier(~p);~n" + "Incorrect enumerant", [?LINE, Ctype], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end, + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case Mod:oe_create_link([Type, OE_THIS, self(), ?get_GlobalQoS(State), + ?get_LocalQoS(State), ?get_MyChannel(State), + ?get_Options(State), ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, PrRef} -> + ProxyID = ?new_Id(State), + NewState = ?add_PushSupplier(State, ProxyID, PrRef, Pid), + {reply, {PrRef, ProxyID}, ?set_IdCounter(NewState, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:obtain_notification_push_supplier();~n" + "Unable to create: ~p/~p~n" + "Reason: ~p", + [?LINE, Mod, Type, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + + +%%----------------------------------------------------------% +%% function : destroy +%% Arguments: - +%% Returns : ok +%%------------------------------------------------------------ +destroy(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, State}. + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {NewQoS, LQS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + admin, ?get_MyChannel(State), + ?get_AllSupplierRefs(State)), + {reply, ok, ?set_BothQoS(State, NewQoS, LQS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + admin, ?get_MyChannel(State), + ?get_AllSupplierRefs(State)), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotifyComm::NotifySubscribe --------- +%%----------------------------------------------------------* +%% function : subscription_change +%% Arguments: +%% Returns : ok | +%% {'EXCEPTION', #'CosNotifyComm_InvalidEventType'{type}} +%%----------------------------------------------------------- +subscription_change(_OE_THIS, _OE_FROM, State, _Added, _Removed) -> + ?DBG("CALLBACK INFORMED: ~p ~p~n",[_Added, _Removed]), + {reply, ok, State}. + +%%----- Inherit from CosNotifyFilter::FilterAdmin ----------- +%%----------------------------------------------------------% +%% function : add_filter +%% Arguments: Filter - CosNotifyFilter::Filter +%% Returns : FilterID - long +%%----------------------------------------------------------- +add_filter(_OE_THIS, _OE_FROM, State, Filter) -> + 'CosNotification_Common':type_check(Filter, 'CosNotifyFilter_Filter'), + FilterID = ?new_Id(State), + NewState = ?set_IdCounter(State, FilterID), + {reply, FilterID, ?add_Filter(NewState, FilterID, Filter)}. + +%%----------------------------------------------------------% +%% function : remove_filter +%% Arguments: FilterID - long +%% Returns : ok +%%----------------------------------------------------------- +remove_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ok, ?del_Filter(State, FilterID)}; +remove_filter(_,_,_,_) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_filter +%% Arguments: FilterID - long +%% Returns : Filter - CosNotifyFilter::Filter | +%% {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}} +%%----------------------------------------------------------- +get_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ?get_Filter(State, FilterID), State}; +get_filter(_,_,_,_) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_filters +%% Arguments: - +%% Returns : Filter - CosNotifyFilter::FilterIDSeq +%%----------------------------------------------------------- +get_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_AllFilterID(State), State}. + +%%----------------------------------------------------------% +%% function : remove_all_filters +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ok, ?del_AllFilter(State)}. + +%%----- Inherit from CosEventChannelAdmin::ConsumerAdmin ---- +%%----------------------------------------------------------% +%% function : obtain_push_supplier +%% Arguments: - +%% Returns : ProxyPushSupplier +%%----------------------------------------------------------- +obtain_push_supplier(OE_THIS, _OE_FROM, State) -> + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case 'CosNotifyChannelAdmin_ProxyPushSupplier':oe_create_link(['PUSH_ANY', OE_THIS, + self(), + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_MyChannel(State), + ?get_Options(State), + ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, PrRef} -> + ProxyID = ?new_Id(State), + NewState = ?add_PushSupplier(State, ProxyID, PrRef, Pid), + {reply, PrRef, ?set_IdCounter(NewState, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:obtain_push_supplier();~n" + "Unable to create: CosNotifyChannelAdmin_ProxyPushSupplier~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : obtain_pull_supplier +%% Arguments: - +%% Returns : ProxyPullSupplier +%%----------------------------------------------------------- +obtain_pull_supplier(OE_THIS, _OE_FROM, State) -> + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case 'CosNotifyChannelAdmin_ProxyPullSupplier':oe_create_link(['PULL_ANY', OE_THIS, + self(), + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_MyChannel(State), + ?get_Options(State), + ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, PrRef} -> + ProxyID = ?new_Id(State), + NewState = ?add_PullSupplier(State, ProxyID, PrRef, Pid), + {reply, PrRef, ?set_IdCounter(NewState, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:obtain_pull_supplier();~n" + "Unable to create: CosNotifyChannelAdmin_ProxyPullSupplier~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +%% To match suppliers +find_obj({value, {_,Obj,_,_}},_) -> Obj; +%% To match filters +find_obj({value, {_,Obj}},_) -> Obj; +find_obj(_, supplier) -> {'EXCEPTION', #'CosNotifyChannelAdmin_ProxyNotFound'{}}; +find_obj(_, filter) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}. + +find_ids(List) -> + find_ids(List, [], false). +find_ids(List, Type) -> + find_ids(List, [], Type). + +find_ids([], Acc, _) -> + Acc; +find_ids([{I,_}|T], Acc, Type) -> + find_ids(T, [I|Acc], Type); +find_ids([{I,_,_,Type}|T], Acc, Type) -> + find_ids(T, [I|Acc], Type); +find_ids([{_I,_,_,_}|T], Acc, Type) -> + find_ids(T, Acc, Type); +find_ids(What, _, _) -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:find_ids();~n" + "Id corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +find_refs(List) -> + find_refs(List, []). + +find_refs([], Acc) -> + Acc; +find_refs([{_,R,_,_}|T], Acc) -> + find_refs(T, [R|Acc]); +find_refs(What, _) -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:find_refs();~n" + "Reference corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +%% Delete a single filter. +%% The list do not differ, i.e., no filter removed, raise exception. +delete_filter(List,List) -> corba:raise(#'CosNotifyFilter_FilterNotFound'{}); +delete_filter(List, _) -> List. + + +%%----------------------------------------------------------- +%% function : callSeq +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callSeq(_OE_THIS, OE_FROM, State, Events, _Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:filter_events(Events, ?get_AllFilter(State)) of + {[], _} when ?is_ANDOP(State) -> + %% Since AND it doesn't matter what the proxies 'think'. Done. + {noreply, State}; + {[], Failed} -> + %% Is OR but the Proxy may allow the events; pass on. + forward(seq, State, Failed, 'MATCH'); + {Passed, _} when ?is_ANDOP(State) -> + %% Since AND we only forward those who passed this objects filters. + forward(seq, State, Passed, 'MATCH'); + {Passed, []} -> + %% Since OR we forward and tell the proxy to do no filtering. + forward(seq, State, Passed, 'MATCHED'); + {Passed, Failed} -> + %% Since OR we forward both and instruct the proxy to check only + %% the ones that failed. + forward(seq, State, Passed, 'MATCHED'), + forward(seq, State, Failed, 'MATCH') + end. + + +%%----------------------------------------------------------- +%% function : callAny +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callAny(_OE_THIS, OE_FROM, State, Event, _Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:filter_events([Event], ?get_AllFilter(State)) of + {[], _} when ?is_ANDOP(State) -> + %% Since AND it doesn't matter what the proxies 'think'. Done. + {noreply, State}; + {[], [Failed]} -> + %% Is OR but the Proxy may allow the event; pass on. + forward(any, State, Failed, 'MATCH'); + {[Passed], _} when ?is_ANDOP(State) -> + %% Since AND we only forward those who passed this objects filters. + forward(any, State, Passed, 'MATCH'); + {[Passed], _} -> + %% Since OR we forward and instruct the proxy to do no checks. + forward(any, State, Passed, 'MATCHED') + end. + + + +%% Forward events +forward(Type, State, Event, Status) -> + forward(Type, ?get_AllSuppliers(State), State, Event, Status). +forward(_, [], State, _, _) -> + {noreply, State}; +forward(any, [{_,H,_,_}|T], State, Event, Status) -> + case catch oe_CosNotificationComm_Event:callAny(H, Event, Status) of + ok -> + ?DBG("CONSUMERADM FORWARD ANY: ~p~n",[Event]), + forward(any, T, State, Event, Status); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:forward();~n" + "Proxy no longer exists; dropping it: ~p", + [?LINE, H], ?DEBUG_LEVEL), + NewState = ?del_SupplierRef(State,H), + forward(any, T, NewState, Event, Status); + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:forward();~n" + "Proxy behaves badly: ~p/~p", + [?LINE, R, H], ?DEBUG_LEVEL), + forward(any, T, State, Event, Status); + R -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:forward();~n" + "Proxy behaves badly: ~p~n" + "Dropping it: ~p", [?LINE, R, H], ?DEBUG_LEVEL), + NewState = ?del_SupplierRef(State, H), + forward(any, T, NewState, Event, Status) + end; +forward(seq, [{_,H,_,_}|T], State, Event, Status) -> + case catch oe_CosNotificationComm_Event:callSeq(H, Event, Status) of + ok -> + ?DBG("CONSUMERADM FORWARD SEQUENCE: ~p~n",[Event]), + forward(seq, T, State, Event, Status); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:forward();~n" + "Proxy no longer exists; dropping it: ~p", + [?LINE, H], ?DEBUG_LEVEL), + NewState = ?del_SupplierRef(State,H), + forward(seq, T, NewState, Event, Status); + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:forward();~n" + "Proxy behaves badly: ~p/~p", [?LINE, R, H], ?DEBUG_LEVEL), + forward(seq, T, State, Event, Status); + R -> + orber:dbg("[~p] CosNotifyChannelAdmin_ConsumerAdmin:forward();~n" + "Proxy behaves badly: ~p~n" + "Dropping it: ~p", [?LINE, R, H], ?DEBUG_LEVEL), + NewState = ?del_SupplierRef(State, H), + forward(seq, T, NewState, Event, Status) + end. + + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotifyChannelAdmin_EventChannelFactory_impl.erl b/lib/cosNotification/src/CosNotifyChannelAdmin_EventChannelFactory_impl.erl new file mode 100644 index 0000000000..872a786f92 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyChannelAdmin_EventChannelFactory_impl.erl @@ -0,0 +1,142 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%------------------------------------------------------------------- +%% File : CosNotifyChannelAdmin_EventChannelFactory_impl.erl +%% Purpose : +%%------------------------------------------------------------------- + +-module('CosNotifyChannelAdmin_EventChannelFactory_impl'). + +%%--------------- INCLUDES ----------------------------------- +%% Application files +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- IMPORTS ------------------------------------ + +%%--------------- EXPORTS ------------------------------------ +%% External +-export([create_channel/5, + get_all_channels/3, + get_event_channel/4]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {adminProp, + idCounter = 0, + options, + etsR, + server_options}). + +%%-----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: See gen_server documentation. +%% Effect : Functions demanded by the gen_server module. +%%------------------------------------------------------------ + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?debug_print("INFO: ~p~n", [Info]), + case Info of + {'EXIT', Pid, normal} -> + ets:match_delete(State#state.etsR, {'_','_',Pid}), + {noreply, State}; + _Other -> + ?debug_print("TERMINATED: ~p~n",[_Other]), + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init(Options) -> + process_flag(trap_exit, true), + SO = 'CosNotification_Common':get_option(server_options, Options, ?not_DEFAULT_SETTINGS), + {ok, #state{options = Options, + etsR = ets:new(oe_ets, [set, protected]), + server_options = SO}}. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% function : create_channel +%% Arguments: InitQoS +%% InitAdmin +%% Returns : Ch - Channel obj ref +%% Id - Channel Id (out-type) +%%----------------------------------------------------------- +create_channel(OE_THIS, _OE_FROM, State, InitQoS, InitAdmin) -> + {QoS, LQoS} = 'CosNotification_Common':init_qos(InitQoS), + {IAdm, LAdm} = 'CosNotification_Common':init_adm(InitAdmin), + Id = 'CosNotification_Common':create_id(State#state.idCounter), + case 'CosNotifyChannelAdmin_EventChannel':oe_create_link([OE_THIS, QoS, IAdm, + LQoS, LAdm, + State#state.options], + [{sup_child, true}|State#state.server_options]) of + {ok, Pid, Ch} -> + ets:insert(State#state.etsR, {Id,Ch,Pid}), + {reply, {Ch, Id}, State#state{idCounter=Id}}; + _ -> + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : get_all_channels +%% Arguments: - +%% Returns : ChannelIDSeq - List of alive channels created +%% by this factory. +%%----------------------------------------------------------- +get_all_channels(_OE_THIS, _OE_FROM, State) -> + {reply, lists:flatten(ets:match(State#state.etsR, {'$1','_','_'})), State}. + +%%----------------------------------------------------------% +%% function : get_event_channel +%% Arguments: ChannelId +%% Returns : ChannelRef | 'CosNotifyChannelAdmin_ChannelNotFound' +%%----------------------------------------------------------- +get_event_channel(_OE_THIS, _OE_FROM, State, Id) -> + {reply, find_obj(ets:lookup(State#state.etsR, Id)), State}. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj([]) -> {'EXCEPTION', #'CosNotifyChannelAdmin_ChannelNotFound'{}}; +find_obj([{_, Obj,_}]) -> Obj; +find_obj(_) -> {'EXCEPTION', #'CosNotifyChannelAdmin_ChannelNotFound'{}}. + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotifyChannelAdmin_EventChannel_impl.erl b/lib/cosNotification/src/CosNotifyChannelAdmin_EventChannel_impl.erl new file mode 100644 index 0000000000..f37a97c4a7 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyChannelAdmin_EventChannel_impl.erl @@ -0,0 +1,721 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%------------------------------------------------------------------- +%% File : CosNotifyChannelAdmin_EventChannel_impl.erl +%% Purpose : +%% Created : 28 Sep 1999 +%%------------------------------------------------------------------- + +-module('CosNotifyChannelAdmin_EventChannel_impl'). + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::EventChannel ------------------ +-export([new_for_consumers/4, + new_for_suppliers/4, + get_consumeradmin/4, + get_supplieradmin/4, + get_all_consumeradmins/3, + get_all_supplieradmins/3]). + +%% Attributes (external) +-export(['_get_MyFactory'/3, + '_get_default_consumer_admin'/3, + '_get_default_supplier_admin'/3, + '_get_default_filter_factory'/3]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotification::AdminPropertiesAdmin --- +-export([get_admin/3, + set_admin/4]). + +%%----- Inherit from CosEventChannelAdmin::EventChannel ------ +-export([for_consumers/3, + for_suppliers/3, + destroy/3]). +%%--------------- Internal ----------------------------------- +%%----- Inherit from cosNotificationComm --------------------- +-export([callAny/5, + callSeq/5]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%% Data structures +-record(state, {myFac, + myConsumers = [], + defConsumerAdmin, + defSupplierAdmin, + defConsumerAdminPid, + defSupplierAdminPid, + defFilterFac, + defFilterFacPid, + etsR, + qosLocal, + qosGlobal, + admGlobal, + options, + idCounter = 0, + 'MaxQueueLength', + 'MaxConsumers', + 'MaxSuppliers'}). +%% Data structures constructors +-define(get_InitState(My, Fil, FilPid, QoS, LQoS, GA, MQ, MC, MS, O), + #state{myFac = My, + defFilterFac = Fil, + defFilterFacPid = FilPid, + etsR = ets:new(oe_ets, [set, protected]), + qosLocal = LQoS, + qosGlobal = QoS, + admGlobal = GA, + options = O, + 'MaxQueueLength' = MQ, + 'MaxConsumers' = MC, + 'MaxSuppliers' = MS}). + +%% NOTE!!! +%% When forwarding events, the objects we will contact is ONLY ConsumerAdmins!! +%% Hence, we will store SupplierAdmins and ConsumerAdmins differently. SupplierAdmins +%% we store in our ets-table while the ConsumerAdmins will be stored on the local +%% State. + +%% Data structures selectors +-define(get_supplierAdmins(S), [S#state.defSupplierAdmin| + lists:flatten(ets:match(S#state.etsR, + {'_','$1','_'}))]). +-define(get_consumerAdmins(S), [{0, S#state.defConsumerAdmin,S#state.defConsumerAdminPid} + |S#state.myConsumers]). +-define(get_allAdmins(S), [S#state.defSupplierAdmin, + S#state.defConsumerAdmin| + lists:flatten(ets:match(S#state.etsR, + {'_','$1','_'}))++ + find_field(S#state.myConsumers, 2)]). +-define(get_consumerAdmIDs(S), [0|find_field(S#state.myConsumers, 1)]). +-define(get_supplierAdmIDs(S), [0|lists:flatten(ets:match(S#state.etsR, + {'$1','_','_'}))]). + +-define(get_supplierAdmin(S, I), find_obj(ets:lookup(S#state.etsR, I))). +-define(get_consumerAdmin(S, I), find_obj(lists:keysearch(I,1,S#state.myConsumers))). +-define(get_supplierAmount(S), ets:info(S#state.etsR, size)). +-define(get_consumerAmount(S), length(S#state.myConsumers)). + +-define(get_MyFactory(S), S#state.myFac). +-define(get_defConsumerAdm(S), S#state.defConsumerAdmin). +-define(get_defSupplierAdm(S), S#state.defSupplierAdmin). +-define(get_defConsumerAdmPid(S), S#state.defConsumerAdminPid). +-define(get_defSupplierAdmPid(S), S#state.defSupplierAdminPid). +-define(get_defFilterFac(S), S#state.defFilterFac). +-define(get_defFilterFacPid(S), S#state.defFilterFacPid). +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +-define(get_GlobalAdm(S), S#state.admGlobal). +-define(get_Options(S), S#state.options). +-define(get_MaxQueueLength(S), S#state.'MaxQueueLength'). +-define(get_MaxConsumers(S), S#state.'MaxConsumers'). +-define(get_MaxSuppliers(S), S#state.'MaxSuppliers'). +-define(get_IdCounter(S), S#state.idCounter). + +%% Data structures modifiers +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +-define(set_GlobalAdm(S,D), S#state{admGlobal=D}). +-define(set_AllAdminP(S,GD, MQ, MC, MS), + S#state{admGlobal=GD,'MaxQueueLength'=MQ, + 'MaxConsumers'=MC, 'MaxSuppliers'=MS}). +-define(set_MaxQueueLength(S,D), S#state{'MaxQueueLength'=D}). +-define(set_MaxConsumers(S,D), S#state{'MaxConsumers'=D}). +-define(set_MaxSuppliers(S,D), S#state{'MaxSuppliers'=D}). +-define(set_defConsumerAdm(S,D,P), S#state{defConsumerAdmin=D, defConsumerAdminPid=P}). +-define(set_defSupplierAdm(S,D,P), S#state{defSupplierAdmin=D, defSupplierAdminPid=P}). +-define(set_defFilterFac(S,D,P), S#state{defFilterFac=D, defFilterFacPid=P}). +-define(add_supplierAdmin(S,I,O,P), ets:insert(S#state.etsR, {I,O,P})). +-define(add_consumerAdmin(S,I,O,P), S#state{myConsumers= [{I,O,P}|S#state.myConsumers]}). +-define(del_supplierAdmin(S,I), ets:delete(S#state.etsR, I)). +-define(del_consumerAdmin(S,I), S#state{myConsumers= + lists:keydelete(I, 1, + S#state.myConsumers)}). +-define(del_consumerAdminRef(S,O), S#state{myConsumers= + lists:keydelete(O, 2, + S#state.myConsumers)}). +-define(del_AdminPid(S,P), delete_obj(S, P)). +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). +%% MISC +-define(is_UndefDefConsAdm(S), S#state.defConsumerAdmin == undefined). +-define(is_UndefDefSuppAdm(S), S#state.defSupplierAdmin == undefined). +-define(is_UndefDefFilterFac(S), S#state.defFilterFac == undefined). +-define(is_PersistentConnection(S), + ?not_GetConnectionReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_PersistentEvent(S), + ?not_GetEventReliability((S#state.qosLocal)) == ?not_Persistent). + +%%-----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%------------------------------------------------------------ + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?DBG("INFO: ~p~n", [Info]), + case Info of + {'EXIT', Pid, _Reason} when ?get_defConsumerAdmPid(State) == Pid -> + {noreply, ?set_defConsumerAdm(State, undefined, undefined)}; + {'EXIT', Pid, _Reason} when ?get_defSupplierAdmPid(State) == Pid -> + {noreply, ?set_defSupplierAdm(State, undefined, undefined)}; + {'EXIT', Pid, _Reason} when ?get_defFilterFacPid(State) == Pid -> + {noreply, ?set_defFilterFac(State, undefined, undefined)}; + {'EXIT', Pid, normal} -> + {noreply, ?del_AdminPid(State, Pid)}; + _Other -> + ?DBG("TERMINATED: ~p~n",[_Other]), + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([MyFac, InitQoS, InitAdmin, LocalQoS, [MaxQ, MaxC, MaxS|_], Options]) -> + process_flag(trap_exit, true), + SO = 'CosNotification_Common':get_option(server_options, Options, + ?not_DEFAULT_SETTINGS), + + ?DBG("CHANNEL INIT STATE:~n~p~n~p~n~p ~p ~p ~p~n~p~n", + [InitQoS, InitAdmin, LocalQoS,MaxQ, MaxC, MaxS, Options]), + + %% Both default Admin:s have the unique id 0 (OMG spec, 98-11-01, + %% Notification p 148), hence, no need to create a unique id. + %% We don't have acces to OE_THIS in this stage so we cannot create the objects + %% now, even though the specification states that. + %% DefConAdm = 'CosNotifyChannelAdmin_ConsumerAdmin':oe_create_link([0],[]), + %% DefSupAdm = 'CosNotifyChannelAdmin_SupplierAdmin':oe_create_link([0],[]), + + case 'CosNotifyFilter_FilterFactory':oe_create_link([], [{sup_child, true}|SO]) of + {ok, Pid, DefFiFac} -> + {ok, ?get_InitState(MyFac, DefFiFac, Pid, InitQoS, LocalQoS, + InitAdmin, MaxQ, MaxC, MaxS, Options)}; + Reason -> + {stop, Reason} + end. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_EventChannel attributes ------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyFactory' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyFactory'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyFactory(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_default_consumer_admin' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_default_consumer_admin'(OE_THIS, _OE_FROM, State) + when ?is_UndefDefConsAdm(State) -> + Op = 'CosNotification_Common':get_option(filterOp, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_ConsumerAdmin':oe_create_link([0, OE_THIS, + self(), Op, + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, DefConAdm} -> + {reply, DefConAdm, ?set_defConsumerAdm(State, DefConAdm, Pid)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:_get_default_consumer_admin();~n" + "Unable to create: CosNotifyChannelAdmin_ConsumerAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end; +'_get_default_consumer_admin'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_defConsumerAdm(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_default_supplier_admin' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_default_supplier_admin'(OE_THIS, _OE_FROM, State) + when ?is_UndefDefSuppAdm(State) -> + Op = 'CosNotification_Common':get_option(filterOp, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_SupplierAdmin':oe_create_link([0, OE_THIS, + self(), Op, + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, DefSupAdm} -> + {reply, DefSupAdm, ?set_defSupplierAdm(State, DefSupAdm, Pid)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:_get_default_supplier_admin();~n" + "Unable to create: CosNotifyChannelAdmin_SupplierAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end; +'_get_default_supplier_admin'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_defSupplierAdm(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_default_filter_factory' +%% Type : readonly +%% Returns : +%%---------------------------------------------------------- +'_get_default_filter_factory'(_OE_THIS, _OE_FROM, State) + when ?is_UndefDefFilterFac(State) -> + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyFilter_FilterFactory':oe_create_link([], [{sup_child, true}|SO]) of + {ok, Pid, DefFiFac} -> + {reply, DefFiFac, ?set_defFilterFac(State, DefFiFac, Pid)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:_get_default_filter_factory();~n" + "Unable to create: CosNotifyChannelAdmin_FilterFactory.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end; +'_get_default_filter_factory'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_defFilterFac(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% function : new_for_consumers +%% Arguments: Op - InterFilterGroupOperator: 'AND_OP' | 'OR_OP' +%% Determines if the Admin:s proxy-children will +%% use AND or OR when checking Filters. +%% Returns : ConsAdm +%% AdminId (out) +%%----------------------------------------------------------- +new_for_consumers(OE_THIS, _OE_FROM, State, Op) -> + is_admin_limit_reached(?get_MaxConsumers(State), ?get_consumerAmount(State)), + AdminId = ?new_Id(State), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_ConsumerAdmin':oe_create_link([AdminId, OE_THIS, + self(), Op, + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, AdminCo} -> + %% Due to different storage, adding a new consumer is NOT done in the + %% same way as for suppliers. + NewState = ?add_consumerAdmin(State, AdminId, AdminCo, Pid), + {reply, {AdminCo, AdminId}, ?set_IdCounter(NewState, AdminId)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:nef_for_consumers();~n" + "Unable to create: CosNotifyChannelAdmin_ConsumerAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : new_for_suppliers +%% Arguments: Op - InterFilterGroupOperator: 'AND_OP' | 'OR_OP' +%% Determines if the Admin:s proxy-children will +%% use AND or OR when checking Filters. +%% Returns : SuppAdm +%% AdminId (out) +%%----------------------------------------------------------- +new_for_suppliers(OE_THIS, _OE_FROM, State, Op) -> + is_admin_limit_reached(?get_MaxSuppliers(State), ?get_supplierAmount(State)), + AdminId = ?new_Id(State), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_SupplierAdmin':oe_create_link([AdminId, OE_THIS, + self(), Op, + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, AdminSu} -> + %% Due to different storage, adding a new supplier is NOT done in the + %% same way as for consumers. + ?add_supplierAdmin(State, AdminId, AdminSu, Pid), + {reply, {AdminSu, AdminId}, ?set_IdCounter(State, AdminId)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:new_for_suppliers();~n" + "Unable to create: CosNotifyChannelAdmin_SupplierAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : get_consumeradmin +%% Arguments: AdminId +%% Returns : ConsAdmin +%%----------------------------------------------------------- +get_consumeradmin(OE_THIS, _OE_FROM, State, 0) when ?is_UndefDefConsAdm(State) -> + Op = 'CosNotification_Common':get_option(filterOp, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_ConsumerAdmin':oe_create_link([0, OE_THIS, + self(), Op, + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, DefConAdm} -> + {reply, DefConAdm, ?set_defConsumerAdm(State, DefConAdm, Pid)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:get_consumer_admin();~n" + "Unable to create: CosNotifyChannelAdmin_ConsumerAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end; +get_consumeradmin(_OE_THIS, _OE_FROM, State, 0) -> + {reply, ?get_defConsumerAdm(State), State}; +get_consumeradmin(_OE_THIS, _OE_FROM, State, AdminId) when is_integer(AdminId) -> + {reply, ?get_consumerAdmin(State, AdminId), State}; +get_consumeradmin(_, _, _, What) -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:get_consumeradmin(~p);~n" + "Not an integer", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_supplieradmin +%% Arguments: AdminId +%% Returns : +%%----------------------------------------------------------- +get_supplieradmin(OE_THIS, _OE_FROM, State, 0) when ?is_UndefDefSuppAdm(State) -> + Op = 'CosNotification_Common':get_option(filterOp, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_SupplierAdmin':oe_create_link([0, OE_THIS, + self(), Op, + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, DefSupAdm} -> + {reply, DefSupAdm, ?set_defSupplierAdm(State, DefSupAdm, Pid)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:get_supplieradmin();~n" + "Unable to create: CosNotifyChannelAdmin_SupplierAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end; +get_supplieradmin(_OE_THIS, _OE_FROM, State, 0) -> + {reply, ?get_defSupplierAdm(State), State}; +get_supplieradmin(_OE_THIS, _OE_FROM, State, AdminId) when is_integer(AdminId) -> + {reply, ?get_supplierAdmin(State, AdminId), State}; +get_supplieradmin(_, _, _, What) -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:get_supplieradmin(~p);~n" + "Not an integer", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_consumeradmins +%% Arguments: - +%% Returns : AdminIDSeq - a list of all unique ID:s. +%%----------------------------------------------------------- +get_all_consumeradmins(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_consumerAdmIDs(State), State}. + +%%----------------------------------------------------------% +%% function : get_all_supplieradmins +%% Arguments: - +%% Returns : AdminIDSeq - a list of all unique ID:s. +%%----------------------------------------------------------- +get_all_supplieradmins(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_supplierAdmIDs(State), State}. + + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: - +%% Returns : CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {GQoS,LQoS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + channel, false, + ?get_allAdmins(State)), + {reply, ok, ?set_BothQoS(State, GQoS,LQoS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} | +%% {ok, CosNotification::NamedPropertyRangeSeq} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + channel, false, + ?get_allAdmins(State)), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotification::AdminPropertiesAdmin -- +%%-----------------------------------------------------------% +%% function : get_admin +%% Arguments: - +%% Returns : AdminProperties +%%----------------------------------------------------------- +get_admin(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalAdm(State), State}. + +%%----------------------------------------------------------% +%% function : set_admin +%% Arguments: Admin +%% Returns : +%%----------------------------------------------------------- +set_admin(_OE_THIS, _OE_FROM, State, Admin) -> + {GAdm,[MaxQ, MaxC, MaxS|_]} = + 'CosNotification_Common':set_adm(Admin, ?get_GlobalAdm(State)), + {reply, ok, ?set_AllAdminP(State, GAdm, MaxQ, MaxC, MaxS)}. + +%%----- Inherit from CosEventChannelAdmin::EventChannel ----- +%%----------------------------------------------------------% +%% function : for_consumers +%% Arguments: - +%% Returns : ConsAdm +%%----------------------------------------------------------- +for_consumers(OE_THIS, _OE_FROM, State) -> + is_admin_limit_reached(?get_MaxConsumers(State), ?get_consumerAmount(State)), + AdminId = ?new_Id(State), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_ConsumerAdmin':oe_create_link([AdminId, OE_THIS, + self(), 'AND_OP', + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, AdminCo} -> + %% Due to different storage, adding a new consumer is NOT done in the + %% same way as for suppliers. + NewState = ?add_consumerAdmin(State, AdminId, AdminCo, Pid), + {reply, AdminCo, ?set_IdCounter(NewState, AdminId)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:for_consumers();~n" + "Unable to create: CosNotifyChannelAdmin_ConsumerAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : for_suppliers +%% Arguments: - +%% Returns : SuppAdm +%%----------------------------------------------------------- +for_suppliers(OE_THIS, _OE_FROM, State) -> + is_admin_limit_reached(?get_MaxSuppliers(State), ?get_supplierAmount(State)), + AdminId = ?new_Id(State), + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_SupplierAdmin':oe_create_link([AdminId, OE_THIS, + self(), 'AND_OP', + ?get_GlobalQoS(State), + ?get_LocalQoS(State), + ?get_Options(State)], + [{sup_child, true}|SO]) of + {ok, Pid, AdminSu} -> + %% Due to different storage, adding a new supplier is NOT done in the + %% same way as for consumers. + ?add_supplierAdmin(State, AdminId, AdminSu, Pid), + {reply, AdminSu, ?set_IdCounter(State, AdminId)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:for_suppliers();~n" + "Unable to create: CosNotifyChannelAdmin_SupplierAdmin.~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%-----------------------------------------------------------% +%% function : destroy +%% Arguments: - +%% Returns : ok +%%------------------------------------------------------------ +destroy(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, State}. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj([]) -> {'EXCEPTION', #'CosNotifyChannelAdmin_AdminNotFound'{}}; +find_obj([{_, Obj,_}]) -> Obj; +find_obj({value, {_, Obj,_}}) -> Obj; +find_obj(_) -> {'EXCEPTION', #'CosNotifyChannelAdmin_AdminNotFound'{}}. + + +find_field(List, Field) -> + find_field(List, Field, []). + +find_field([], _, Acc) -> + Acc; +find_field([{I,_,_}|T], 1, Acc) -> + find_field(T, 1, [I|Acc]); +find_field([{_,O,_}|T], 2, Acc) -> + find_field(T, 2, [O|Acc]); +% Left out for now to avoid dialyzer warning. +%find_field([{_,_,P}|T], 3, Acc) -> +% find_field(T, 3, [P|Acc]); +find_field(What, _, _) -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:find_field();~n" + "Data corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +delete_obj(State, Pid) -> + case ets:match_object(State#state.etsR, {'_', '_', Pid}) of + [] -> + State#state{myConsumers=lists:keydelete(Pid, 3, State#state.myConsumers)}; + [{Id,_Obj,Pid}] -> + ets:delete(State#state.etsR, Id), + State + end. + +%% Test if we have reached limit for Admin objects. +is_admin_limit_reached(0, _) -> + %% When set to zero it means that there is no limit. + ok; +is_admin_limit_reached(Max, Current) when Current >= Max -> + %% The Current value do not include the default Admin objects, hence + %% we use >= instead of >. + corba:raise(#'IMP_LIMIT'{completion_status=?COMPLETED_NO}); +is_admin_limit_reached(_, _) -> + ok. + +%%----------------------------------------------------------- +%% function : callSeq +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callSeq(_OE_THIS, OE_FROM, State, Event, Status) -> + corba:reply(OE_FROM, ok), + forward(seq, State, Event, Status). + +%%----------------------------------------------------------- +%% function : callAny +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callAny(_OE_THIS, OE_FROM, State, Event, Status) -> + corba:reply(OE_FROM, ok), + forward(any, State, Event, Status). + +%% Forward events +forward(Type, State, Event, Status) -> + forward(Type, ?get_consumerAdmins(State), State, Event, Status). + +forward(_, [], State, _, _) -> + {noreply, State}; +forward(Type, [{_,undefined,_}|T], State, Event, Status) -> + %% Match if no default objects associated. + forward(Type, T, State, Event, Status); +forward(any, [{_,H,_}|T], State, Event, Status) -> + case catch oe_CosNotificationComm_Event:callAny(H, Event, Status) of + ok -> + ?DBG("CHANNEL FORWARD ANY: ~p~n",[Event]), + forward(any, T, State, Event, Status); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:forward();~n" + "Admin no longer exists; dropping it: ~p", + [?LINE, H], ?DEBUG_LEVEL), + NewState = ?del_consumerAdminRef(State,H), + forward(any, T, NewState, Event, Status); + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:forward();~n" + "Admin behaves badly: ~p/~p", + [?LINE, R, H], ?DEBUG_LEVEL), + forward(any, T, State, Event, Status); + R -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:forward();~n" + "Admin behaves badly: ~p~n" + "Dropping it: ~p", [?LINE, R, H], ?DEBUG_LEVEL), + NewState = ?del_consumerAdminRef(State, H), + forward(any, T, NewState, Event, Status) + end; +forward(seq, [{_,H,_}|T], State, Event, Status) -> + case catch oe_CosNotificationComm_Event:callSeq(H, Event, Status) of + ok -> + ?DBG("CHANNEL FORWARD SEQUENCE: ~p~n",[Event]), + forward(seq, T, State, Event, Status); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:forward();~n" + "Admin no longer exists; dropping it: ~p", + [?LINE, H], ?DEBUG_LEVEL), + NewState = ?del_consumerAdminRef(State,H), + forward(seq, T, NewState, Event, Status); + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:forward();~n" + "Admin behaves badly: ~p/~p", + [?LINE, R, H], ?DEBUG_LEVEL), + forward(seq, T, State, Event, Status); + R -> + orber:dbg("[~p] CosNotifyChannelAdmin_EventChannel:forward();~n" + "Admin behaves badly: ~p~n" + "Dropping it: ~p", [?LINE, R, H], ?DEBUG_LEVEL), + NewState = ?del_consumerAdminRef(State,H), + forward(seq, T, NewState, Event, Status) + end. + + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotifyChannelAdmin_SupplierAdmin_impl.erl b/lib/cosNotification/src/CosNotifyChannelAdmin_SupplierAdmin_impl.erl new file mode 100644 index 0000000000..1c3f3d8d0b --- /dev/null +++ b/lib/cosNotification/src/CosNotifyChannelAdmin_SupplierAdmin_impl.erl @@ -0,0 +1,579 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%------------------------------------------------------------------- +%% File : CosNotifyChannelAdmin_SupplierAdmin_impl.erl +%% Purpose : +%%------------------------------------------------------------------- + +-module('CosNotifyChannelAdmin_SupplierAdmin_impl'). + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::SupplierAdmin ----------------- +-export([get_proxy_consumer/4, + obtain_notification_pull_consumer/4, + obtain_notification_push_consumer/4, + destroy/3]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotifyComm::NotifyPublish ------------ +-export([offer_change/5]). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ------------ +-export([add_filter/4, + remove_filter/4, + get_filter/4, + get_all_filters/3, + remove_all_filters/3]). + +%%----- Inherit from CosEventChannelAdmin::SupplierAdmin ----- +-export([obtain_push_consumer/3, + obtain_pull_consumer/3]). + +%% Attributes (external) +-export(['_get_MyID'/3, + '_get_MyChannel'/3, + '_get_MyOperator'/3, + '_get_pull_consumers'/3, + '_get_push_consumers'/3]). + +%%--------------- Internal ----------------------------------- +%%----- Inherit from cosNotificationComm --------------------- +-export([callAny/5, + callSeq/5]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {myId, + myChannel, + myChannelPid, + myOperator, + myFilters = [], + idCounter = 0, + etsR, + qosGlobal, + qosLocal, + options}). + +%% Data structures constructors +-define(get_InitState(_MyID, _MyCh, _MyChP, _MyOp, _QoS, _LQS, _O), + #state{myId = _MyID, + myChannel = _MyCh, + myChannelPid = _MyChP, + myOperator = _MyOp, + qosGlobal = _QoS, + qosLocal = _LQS, + options = _O, + etsR = ets:new(oe_ets, [set, protected])}). + +%% Data structures selectors +-define(get_PushConsumers(S), lists:flatten(ets:match(S#state.etsR, + {'_','$1','_',pusher}))). +-define(get_PullConsumers(S), lists:flatten(ets:match(S#state.etsR, + {'_','$1','_',puller}))). +-define(get_AllConsumers(S), lists:flatten(ets:match(S#state.etsR, + {'_','$1','_','_'}))). +-define(get_PushConsumerIDs(S), lists:flatten(ets:match(S#state.etsR, + {'$1','_','_',pusher}))). +-define(get_PullConsumerIDs(S), lists:flatten(ets:match(S#state.etsR, + {'$1','_','_',puller}))). + +-define(get_Consumer(S, I), find_obj(ets:lookup(S#state.etsR, I), consumer)). + +-define(get_MyID(S), S#state.myId). +-define(get_MyChannel(S), S#state.myChannel). +-define(get_MyChannelPid(S), S#state.myChannelPid). +-define(get_MyOperator(S), S#state.myOperator). +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +-define(get_Filter(S, I), find_obj(lists:keysearch(I, 1, S#state.myFilters), + filter)). +-define(get_AllFilter(S), S#state.myFilters). +-define(get_AllFilterID(S), find_ids(S#state.myFilters)). +-define(get_Options(S), S#state.options). +-define(get_IdCounter(S), S#state.idCounter). + +%% Data structures modifiers +-define(set_LocalQoS(S,D), S#state{qosLocal=D}). +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +-define(add_PushConsumer(S,I,R,P),ets:insert(State#state.etsR, {I,R,P,pusher})). +-define(add_PullConsumer(S,I,R,P),ets:insert(State#state.etsR, {I,R,P,puller})). +-define(del_Consumer(S,I), ets:delete(S#state.etsR, I)). +-define(del_ConsumerPid(S,P), ets:match_delete(S#state.etsR, {'_','_',P,'_'})). +-define(add_Filter(S,I,O), S#state{myFilters=[{I,O}|S#state.myFilters]}). +-define(del_Filter(S,I), S#state{myFilters= + delete_filter(lists:keydelete(I, 1, + S#state.myFilters), + S#state.myFilters)}). +-define(del_AllFilter(S), S#state{myFilters=[]}). +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). + +%% MISC +-define(is_PersistentEvent(S), + ?not_GetEventReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_PersistentConnection(S), + ?not_GetConnectionReliability((S#state.qosLocal)) == ?not_Persistent). + +%%----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%----------------------------------------------------------- + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + case Info of + {'EXIT', Pid, Reason} when ?get_MyChannelPid(State) == Pid -> + ?DBG("PARENT CHANNEL: ~p TERMINATED.~n",[Reason]), + {stop, Reason, State}; + {'EXIT', Pid, normal} -> + ?del_ConsumerPid(State, Pid), + {noreply, State}; + _Other -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([MyId, MyChannel, MyChannelPid, MyOperator, InitQoS, LQS, Options]) -> + process_flag(trap_exit, true), + {ok, ?get_InitState(MyId, MyChannel, MyChannelPid, MyOperator, InitQoS, LQS, Options)}. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_ConsumerAdmin attributes ------ +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyID' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyID'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyID(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyChannel' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyChannel'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyChannel(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyOperator' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyOperator'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyOperator(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_pull_consumers' +%% Type : readonly +%% Returns : ProxyIDSeq +%%----------------------------------------------------------- +'_get_pull_consumers'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PullConsumerIDs(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_push_consumers' +%% Type : readonly +%% Returns : ProxyIDSeq +%%----------------------------------------------------------- +'_get_push_consumers'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PushConsumerIDs(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% function : get_proxy_consumer +%% Arguments: ProxyId - unique identifier (long) +%% Returns : ObjRef | {'EXCEPTION', #'ProxyNotFound'{}} +%%----------------------------------------------------------- +get_proxy_consumer(_OE_THIS, _OE_FROM, State, ProxyId) -> + {reply, ?get_Consumer(State, ProxyId), State}. + +%%----------------------------------------------------------% +%% function : obtain_notification_pull_consumer +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +obtain_notification_pull_consumer(OE_THIS, _OE_FROM, State, Ctype) -> + %% Choose which module to use. + {Mod, Type} = + case Ctype of + 'ANY_EVENT' -> + {'CosNotifyChannelAdmin_ProxyPullConsumer', 'PULL_ANY'}; + 'STRUCTURED_EVENT' -> + {'CosNotifyChannelAdmin_StructuredProxyPullConsumer', 'PULL_STRUCTURED'}; + 'SEQUENCE_EVENT' -> + {'CosNotifyChannelAdmin_SequenceProxyPullConsumer', 'PULL_SEQUENCE'}; + _ -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:" + "obtain_notification_pull_consumer(~p);~n" + "Incorrect enumerant", + [?LINE, Ctype], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end, + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch Mod:oe_create_link([Type, OE_THIS, self(), ?get_GlobalQoS(State), + ?get_LocalQoS(State), ?get_MyChannel(State), + ?get_Options(State), ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, Proxy} -> + ProxyID = ?new_Id(State), + ?add_PullConsumer(State, ProxyID, Proxy, Pid), + {reply, {Proxy, ProxyID}, ?set_IdCounter(State, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:" + "obtain_notification_pull_consumer();~n" + "Unable to create: ~p/~p~n" + "Reason: ~p", [?LINE, Mod, Type, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : obtain_notification_push_supplier +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +obtain_notification_push_consumer(OE_THIS, _OE_FROM, State, Ctype) -> + %% Choose which module to use. + {Mod, Type} = + case Ctype of + 'ANY_EVENT' -> + {'CosNotifyChannelAdmin_ProxyPushConsumer', 'PUSH_ANY'}; + 'STRUCTURED_EVENT' -> + {'CosNotifyChannelAdmin_StructuredProxyPushConsumer', 'PUSH_STRUCTURED'}; + 'SEQUENCE_EVENT' -> + {'CosNotifyChannelAdmin_SequenceProxyPushConsumer', 'PUSH_SEQUENCE'}; + _ -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:" + "obtain_notification_push_consumer(~p);~n" + "Incorrect enumerant", [?LINE, Ctype], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end, + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch Mod:oe_create_link([Type, OE_THIS, self(), ?get_GlobalQoS(State), + ?get_LocalQoS(State), ?get_MyChannel(State), + ?get_Options(State), ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, Proxy} -> + ProxyID = ?new_Id(State), + ?add_PushConsumer(State, ProxyID, Proxy, Pid), + {reply, {Proxy, ProxyID}, ?set_IdCounter(State, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:" + "obtain_notification_push_consumer();~n" + "Unable to create: ~p/~p~n" + "Reason: ~p", [?LINE, Mod, Type, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : destroy +%% Arguments: - +%% Returns : ok +%%------------------------------------------------------------ +destroy(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, State}. + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {NewQoS, LQS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + admin, ?get_MyChannel(State), + ?get_AllConsumers(State)), + {reply, ok, ?set_BothQoS(State, NewQoS, LQS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} +%% {ok, CosNotification::NamedPropertyRangeSeq} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + admin, ?get_MyChannel(State), + ?get_AllConsumers(State)), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotifyComm::NotifyPublish ----------- +%%----------------------------------------------------------* +%% function : offer_change +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +offer_change(_OE_THIS, _OE_FROM, State, _Added, _Removed) -> + {reply, ok, State}. + +%%----- Inherit from CosNotifyFilter::FilterAdmin ----------- +%%----------------------------------------------------------% +%% function : add_filter +%% Arguments: Filter - CosNotifyFilter::Filter +%% Returns : FilterID - long +%%----------------------------------------------------------- +add_filter(_OE_THIS, _OE_FROM, State, Filter) -> + 'CosNotification_Common':type_check(Filter, 'CosNotifyFilter_Filter'), + FilterID = ?new_Id(State), + NewState = ?set_IdCounter(State, FilterID), + {reply, FilterID, ?add_Filter(NewState, FilterID, Filter)}. + +%%----------------------------------------------------------% +%% function : remove_filter +%% Arguments: FilterID - long +%% Returns : ok +%%----------------------------------------------------------- +remove_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ok, ?del_Filter(State, FilterID)}; +remove_filter(_,_,_,What) -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:remove_filter(~p);~n" + "Not an integer", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_filter +%% Arguments: FilterID - long +%% Returns : Filter - CosNotifyFilter::Filter | +%% {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}} +%%----------------------------------------------------------- +get_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ?get_Filter(State, FilterID), State}; +get_filter(_,_,_,What) -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:get_filter(~p);~n" + "Not an integer", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_filters +%% Arguments: - +%% Returns : Filter - CosNotifyFilter::FilterIDSeq +%%----------------------------------------------------------- +get_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_AllFilterID(State), State}. + +%%----------------------------------------------------------% +%% function : remove_all_filters +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ok, ?del_AllFilter(State)}. + +%%----- Inherit from CosEventChannelAdmin::SupplierAdmin ---- +%%----------------------------------------------------------% +%% function : obtain_push_consumer +%% Arguments: - +%% Returns : ProxyPushConsumer +%%----------------------------------------------------------- +obtain_push_consumer(OE_THIS, _OE_FROM, State) -> + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_ProxyPushConsumer': + oe_create_link(['PUSH_ANY', OE_THIS, self(), ?get_GlobalQoS(State), + ?get_LocalQoS(State), ?get_MyChannel(State), + ?get_Options(State), ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, Proxy} -> + ProxyID = ?new_Id(State), + ?add_PushConsumer(State, ProxyID, Proxy, Pid), + {reply, Proxy, ?set_IdCounter(State, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:obtain_push_consumer();~n" + "Unable to create: CosNotifyChannelAdmin_ProxyPushConsumer~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%----------------------------------------------------------% +%% function : obtain_pull_consumer +%% Arguments: - +%% Returns : ProxyPullConsumer +%%----------------------------------------------------------- +obtain_pull_consumer(OE_THIS, _OE_FROM, State) -> + SO = 'CosNotification_Common':get_option(server_options, ?get_Options(State), + ?not_DEFAULT_SETTINGS), + case catch 'CosNotifyChannelAdmin_ProxyPullConsumer': + oe_create_link(['PULL_ANY', OE_THIS, self(), ?get_GlobalQoS(State), + ?get_LocalQoS(State), ?get_MyChannel(State), + ?get_Options(State), ?get_MyOperator(State)], + [{sup_child, true}|SO]) of + {ok, Pid, Proxy} -> + ProxyID = ?new_Id(State), + ?add_PullConsumer(State, ProxyID, Proxy, Pid), + {reply, Proxy, ?set_IdCounter(State, ProxyID)}; + What -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:obtain_push_consumer();~n" + "Unable to create: CosNotifyChannelAdmin_ProxyPullConsumer~n" + "Reason: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj([], consumer) -> {'EXCEPTION', #'CosNotifyChannelAdmin_ProxyNotFound'{}}; +find_obj([], filter) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}; +%% To match consumers +find_obj([{_,Obj,_,_}],_) -> Obj; +%% To match filters +find_obj({value, {_,Obj}},_) -> Obj; +find_obj(_,consumer) -> {'EXCEPTION', #'CosNotifyChannelAdmin_ProxyNotFound'{}}; +find_obj(_,filter) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}. + +find_ids(List) -> find_ids(List, []). +find_ids([], Acc) -> Acc; +find_ids([{I,_}|T], Acc) -> find_ids(T, [I|Acc]); +find_ids(What, _) -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:find_ids();~n" + "Id corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +%% Delete a single filter. +%% The list do not differ, i.e., no filter removed, raise exception. +delete_filter(List,List) -> corba:raise(#'CosNotifyFilter_FilterNotFound'{}); +delete_filter(List, _) -> List. + +%%----------------------------------------------------------- +%% function : callSeq +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callSeq(_OE_THIS, OE_FROM, State, Events, _Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:filter_events(Events, ?get_AllFilter(State)) of + {[], _} -> + {noreply, State}; + {Passed, _} -> + forward(seq, State, Passed, 'MATCHED') + end. + +%%----------------------------------------------------------- +%% function : callAny +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callAny(_OE_THIS, OE_FROM, State, Event, _Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:filter_events([Event], ?get_AllFilter(State)) of + {[], _} -> + {noreply, State}; + {[Passed], _} -> + forward(any, State, Passed, 'MATCHED') + end. + +%% Forward events +forward(any, State, Event, Status) -> + case catch oe_CosNotificationComm_Event:callAny(?get_MyChannel(State), + Event, Status) of + ok -> + ?DBG("SUPPLIERADM FORWARD ANY: ~p~n",[Event]), + {noreply, State}; + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:forward();~n" + "Channel no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + {stop, normal, State}; + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:forward();~n" + "Channel respond incorrect: ~p~n" + "Dropping: ~p", [?LINE, R, Event], ?DEBUG_LEVEL), + {noreply, State}; + R -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:forward();~n" + "Channel respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, R, Event], ?DEBUG_LEVEL), + {stop, normal, State} + end; +forward(seq, State, Event, Status) -> + case catch oe_CosNotificationComm_Event:callSeq(?get_MyChannel(State), + Event, Status) of + ok -> + ?DBG("SUPPLIERADM FORWARD SEQUENCE: ~p~n",[Event]), + {noreply, State}; + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:forward();~n" + "Channel no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + {stop, normal, State}; + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:forward();~n" + "Channel respond incorrect: ~p~n" + "Dropping: ~p", [?LINE, R, Event], ?DEBUG_LEVEL), + {noreply, State}; + R -> + orber:dbg("[~p] CosNotifyChannelAdmin_SupplierAdmin:forward();~n" + "Channel respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, R, Event], ?DEBUG_LEVEL), + {stop, normal, State} + end. + + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotifyComm.cfg b/lib/cosNotification/src/CosNotifyComm.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyComm.cfg diff --git a/lib/cosNotification/src/CosNotifyComm.idl b/lib/cosNotification/src/CosNotifyComm.idl new file mode 100644 index 0000000000..f0386f0982 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyComm.idl @@ -0,0 +1,83 @@ +#ifndef _COS_NOTIFYCOMM_IDL_ +#define _COS_NOTIFYCOMM_IDL_ + +#pragma prefix "omg.org" + + +#include<CosNotification.idl> + +module CosNotifyComm { + exception InvalidEventType { CosNotification::EventType type; }; + + interface NotifyPublish { + void offer_change (in CosNotification::EventTypeSeq added, + in CosNotification::EventTypeSeq removed) + raises (InvalidEventType); + }; // NotifyPublish + + interface NotifySubscribe { + void subscription_change(in CosNotification::EventTypeSeq added, + in CosNotification::EventTypeSeq removed) + raises (InvalidEventType); + }; // NotifySubscribe + + interface PushConsumer : NotifyPublish, CosEventComm::PushConsumer { }; // PushConsumer + + interface PullConsumer : NotifyPublish, CosEventComm::PullConsumer { }; // PullConsumer + + interface PullSupplier : NotifySubscribe, CosEventComm::PullSupplier { }; // PullSupplier + + interface PushSupplier : NotifySubscribe, CosEventComm::PushSupplier { }; + + interface StructuredPushConsumer : NotifyPublish { + void push_structured_event(in CosNotification::StructuredEvent notification) + raises(CosEventComm::Disconnected); + + void disconnect_structured_push_consumer(); + }; // StructuredPushConsumer + + interface StructuredPullConsumer : NotifyPublish { + void disconnect_structured_pull_consumer(); + }; // StructuredPullConsumer + + interface StructuredPullSupplier : NotifySubscribe { + CosNotification::StructuredEvent pull_structured_event() + raises(CosEventComm::Disconnected); + + CosNotification::StructuredEvent try_pull_structured_event(out boolean has_event) + raises(CosEventComm::Disconnected); + + void disconnect_structured_pull_supplier(); + }; // StructuredPullSupplier + + interface StructuredPushSupplier : NotifySubscribe { + void disconnect_structured_push_supplier(); + }; // StructuredPushSupplier + + interface SequencePushConsumer : NotifyPublish { + void push_structured_events(in CosNotification::EventBatch notifications) + raises(CosEventComm::Disconnected); + + void disconnect_sequence_push_consumer(); + }; // SequencePushConsumer + + interface SequencePullConsumer : NotifyPublish { + void disconnect_sequence_pull_consumer(); + }; // SequencePullConsumer + + interface SequencePullSupplier : NotifySubscribe { + CosNotification::EventBatch pull_structured_events(in long max_number) + raises(CosEventComm::Disconnected); + CosNotification::EventBatch try_pull_structured_events(in long max_number, out boolean has_event) + raises(CosEventComm::Disconnected); + + void disconnect_sequence_pull_supplier(); + }; // SequencePullSupplier + + interface SequencePushSupplier : NotifySubscribe { + void disconnect_sequence_push_supplier(); + }; // SequencePushSupplier +}; // CosNotifyComm + +#endif /* ifndef _COS_NOTIFYCOMM_IDL_ */ + diff --git a/lib/cosNotification/src/CosNotifyFilter.cfg b/lib/cosNotification/src/CosNotifyFilter.cfg new file mode 100644 index 0000000000..167550e0f5 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyFilter.cfg @@ -0,0 +1,6 @@ +{this, "CosNotifyFilter::Filter"}. +{{handle_info, "CosNotifyFilter::Filter"}, true}. +{this, "CosNotifyFilter::MappingFilter"}. +{{handle_info, "CosNotifyFilter::MappingFilter"}, true}. +{this, "CosNotifyFilter::FilterFactory"}. +{{handle_info, "CosNotifyFilter::FilterFactory"}, true}. diff --git a/lib/cosNotification/src/CosNotifyFilter.idl b/lib/cosNotification/src/CosNotifyFilter.idl new file mode 100644 index 0000000000..c6498ddcd0 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyFilter.idl @@ -0,0 +1,140 @@ +#ifndef _COS_NOTIFYFILTER_IDL_ +#define _COS_NOTIFYFILTER_IDL_ + +#pragma prefix "omg.org" + +#include<CosNotifyComm.idl> +#include<CosNotification.idl> + +module CosNotifyFilter { + typedef long ConstraintID; + struct ConstraintExp { + CosNotification::EventTypeSeq event_types; + string constraint_expr; + }; + + typedef sequence<ConstraintID> ConstraintIDSeq; + typedef sequence<ConstraintExp> ConstraintExpSeq; + struct ConstraintInfo { + ConstraintExp constraint_expression; + ConstraintID constraint_id; + }; + typedef sequence<ConstraintInfo> ConstraintInfoSeq; + struct MappingConstraintPair { + ConstraintExp constraint_expression; + any result_to_set; + }; + typedef sequence<MappingConstraintPair> MappingConstraintPairSeq; + struct MappingConstraintInfo { + ConstraintExp constraint_expression; + ConstraintID constraint_id; + any value; + }; + typedef sequence<MappingConstraintInfo> MappingConstraintInfoSeq; + typedef long CallbackID; + typedef sequence<CallbackID> CallbackIDSeq; + exception UnsupportedFilterableData {}; + exception InvalidGrammar {}; + exception InvalidConstraint {ConstraintExp constr;}; + exception DuplicateConstraintID {ConstraintID id;}; + exception ConstraintNotFound {ConstraintID id;}; + exception CallbackNotFound {}; + exception InvalidValue {ConstraintExp constr; any value;}; + interface Filter { + readonly attribute string constraint_grammar; + ConstraintInfoSeq add_constraints (in ConstraintExpSeq constraint_list) + raises (InvalidConstraint); + + void modify_constraints (in ConstraintIDSeq del_list, + in ConstraintInfoSeq modify_list) + raises (InvalidConstraint, ConstraintNotFound); + + ConstraintInfoSeq get_constraints(in ConstraintIDSeq id_list) + raises (ConstraintNotFound); + + ConstraintInfoSeq get_all_constraints(); + + void remove_all_constraints(); + void destroy(); + boolean match (in any filterable_data) + raises (UnsupportedFilterableData); + + boolean match_structured (in CosNotification::StructuredEvent filterable_data) + raises (UnsupportedFilterableData); + + boolean match_typed (in CosNotification::PropertySeq filterable_data) + raises (UnsupportedFilterableData); + + CallbackID attach_callback (in CosNotifyComm::NotifySubscribe callback); + + void detach_callback (in CallbackID callback) + raises ( CallbackNotFound ); + + CallbackIDSeq get_callbacks(); + }; // Filter + + interface MappingFilter { + readonly attribute string constraint_grammar; + + readonly attribute CORBA::TypeCode value_type; + + readonly attribute any default_value; + + MappingConstraintInfoSeq add_mapping_constraints (in MappingConstraintPairSeq pair_list) + raises (InvalidConstraint, InvalidValue); + + void modify_mapping_constraints (in ConstraintIDSeq del_list, + in MappingConstraintInfoSeq modify_list) + raises (InvalidConstraint, InvalidValue, ConstraintNotFound); + + MappingConstraintInfoSeq get_mapping_constraints (in ConstraintIDSeq id_list) + raises (ConstraintNotFound); + + MappingConstraintInfoSeq get_all_mapping_constraints(); + + void remove_all_mapping_constraints(); + + void destroy(); + + boolean match (in any filterable_data, out any result_to_set) + raises (UnsupportedFilterableData); + + boolean match_structured (in CosNotification::StructuredEvent filterable_data, out any result_to_set) + raises (UnsupportedFilterableData); + + boolean match_typed (in CosNotification::PropertySeq filterable_data, out any result_to_set) + raises (UnsupportedFilterableData); + }; // MappingFilter + + + interface FilterFactory { + + Filter create_filter (in string constraint_grammar) + raises (InvalidGrammar); + + MappingFilter create_mapping_filter (in string constraint_grammar, in any default_value) + raises(InvalidGrammar); + }; // FilterFactory + + typedef long FilterID; + typedef sequence<FilterID> FilterIDSeq; + exception FilterNotFound {}; + + interface FilterAdmin { + FilterID add_filter (in Filter new_filter); + + void remove_filter (in FilterID filter) + raises (FilterNotFound); + + Filter get_filter (in FilterID filter) + raises (FilterNotFound); + + FilterIDSeq get_all_filters(); + + void remove_all_filters(); + }; // FilterAdmin +}; // CosNotifyFilter + + +#endif /* ifndef _COS_NOTIFYFILTER_IDL_ */ + diff --git a/lib/cosNotification/src/CosNotifyFilter_FilterFactory_impl.erl b/lib/cosNotification/src/CosNotifyFilter_FilterFactory_impl.erl new file mode 100644 index 0000000000..9a7b431513 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyFilter_FilterFactory_impl.erl @@ -0,0 +1,125 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : CosNotifyFilter_FilterFactory_impl.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module('CosNotifyFilter_FilterFactory_impl'). + + +%%--------------- INCLUDES ----------------------------------- +%% Application files +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- IMPORTS ------------------------------------ + +%%--------------- EXPORTS ------------------------------------ +%% External +-export([create_filter/3, + create_mapping_filter/4]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {adminProp, + etsR, + options}). + +%%-----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: See gen_server documentation. +%% Effect : Functions demanded by the gen_server module. +%%------------------------------------------------------------ + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(_Info, State) -> + ?debug_print("INFO: ~p DATA: ~p~n", [State, _Info]), + {noreply, State}. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init(Options) -> + process_flag(trap_exit, true), + {ok, #state{options = Options}}. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% function : create_filter +%% Arguments: InitGrammar - string() +%% Returns : CosNotifyFilter::Filter | +%% {'EXCEPTION', InvalidGrammar} +%%----------------------------------------------------------- +create_filter(OE_THIS, State, InitGrammar) -> + case lists:member(InitGrammar, ?not_SupportedGrammars) of + true -> + SO = 'CosNotification_Common':get_option(server_options, State#state.options, + ?not_DEFAULT_SETTINGS), + Fi='CosNotifyFilter_Filter':oe_create_link([OE_THIS, self(), + InitGrammar], + SO), + {reply, Fi, State}; + _ -> + corba:raise(#'CosNotifyFilter_InvalidGrammar'{}) + end. + +%%----------------------------------------------------------% +%% function : create_mapping_filter +%% Arguments: InitGrammar - string() +%% Returns : CosNotifyFilter::Filter | +%% {'EXCEPTION', InvalidGrammar} +%%----------------------------------------------------------- +create_mapping_filter(OE_THIS, State, InitGrammar, DefVal) -> + case lists:member(InitGrammar, ?not_SupportedGrammars) of + true -> + SO = 'CosNotification_Common':get_option(server_options, State#state.options, + ?not_DEFAULT_SETTINGS), + Fi='CosNotifyFilter_MappingFilter':oe_create_link([OE_THIS, self(), + InitGrammar, DefVal], + SO), + {reply, Fi, State}; + _ -> + corba:raise(#'CosNotifyFilter_InvalidGrammar'{}) + end. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/CosNotifyFilter_Filter_impl.erl b/lib/cosNotification/src/CosNotifyFilter_Filter_impl.erl new file mode 100644 index 0000000000..042e180170 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyFilter_Filter_impl.erl @@ -0,0 +1,670 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : CosNotifyFilter_Filter_impl.erl +%% Purpose : +%% Note : For an event to be forwarded it's sufficient that at least +%% one of the constraints return 'true'. +%% ALL functions in this module are internal. May NOT be used +%% externally in ANY WAY; only CosNotification system modules. +%%---------------------------------------------------------------------- + +-module('CosNotifyFilter_Filter_impl'). + +%%--------------- INCLUDES ----------------------------------- +%% Application files +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- IMPORTS ------------------------------------ + +%%--------------- EXPORTS ------------------------------------ +%% External Attributes +-export(['_get_constraint_grammar'/2]). +%% External Functions +-export([add_constraints/3, + modify_constraints/4, + get_constraints/3, + get_all_constraints/2, + remove_all_constraints/2, + destroy/2, + match/3, + match_structured/3, + match_typed/3, + attach_callback/3, + detach_callback/3, + get_callbacks/2]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%%######### MISC ########## +-define(create_ConstraintInfo(_Types, _Con, _ID), + #'CosNotifyFilter_ConstraintInfo'{ + constraint_expression = + #'CosNotifyFilter_ConstraintExp'{ + event_types = _Types, + constraint_expr = _Con}, + constraint_id = _ID + }). + +%%#### Data structures #### +-record(state, {constraint_grammar, + constraints = [], + filters = [], + callbacks = [], + idCounter = 0, + filterFactory, + factoryPid, + etsR}). + +%% Data structures constructors +-define(get_InitState(Fac, Pid, Gr), + #state{constraint_grammar=Gr, + filterFactory=Fac, + factoryPid=Pid, + etsR = ets:new(oe_ets, [bag, protected])}). + +%%------------------- Data structures selectors ------------------- +%% Grammar +-define(get_Grammar(S), S#state.constraint_grammar). +%% Callbacks +% Left out for now to avoid dialyzer warning. +%-define(get_Callback(S,I), find_obj(lists:keysearch(I, 1, S#state.callbacks), +% callback)). +-define(get_AllCallback(S), lists:map(fun({_V, C}) -> C end, + S#state.callbacks)). +-define(get_AllCallbackID(S), lists:map(fun({V, _C}) -> V end, + S#state.callbacks)). +%% ID:s +-define(get_IdCounter(S), S#state.idCounter). + +%% Constraints +-define(get_Constraint(S,I), find_obj(lists:keysearch(I, 1, S#state.constraints), + constraint)). +-define(get_AllConstraints(S), lists:map(fun({I, C, _W, _WC, _K, T}) -> + ?create_ConstraintInfo(T, C, I) + end, + S#state.constraints)). +-define(get_ConstraintAllData(S), S#state.constraints). +-define(get_ConstraintData(S,I), lists:keysearch(I, 1, S#state.constraints)). +-define(match_Type(S,I,ET), ets:lookup(S#state.etsR, {I, ET})). +%% Parse Tree +-define(get_ParseTree(S,K), find_obj(ets:lookup(S#state.etsR, K), tree)). + +%%------------------- Data structures modifiers ------------------- +%% Callbacks +-define(del_Callback(S,I), S#state{callbacks = + delete_obj(lists:keydelete(I,1, + S#state.callbacks), + S#state.callbacks, callback)}). +-define(del_AllCallbacks(S), S#state{callbacks=[]}). +-define(add_Callback(S,V,C), S#state{idCounter=V, + callbacks = [{V,C}|S#state.callbacks]}). +%% ID:s +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). + +%% Constraints +-define(del_Constraint(S, I), match_delete(S, S#state.constraints, I)). +-define(del_AllConstraints(S), clear_DB(S)). +-define(add_Constraint(S,I,C,W,_WC,K,T), S#state{constraints = + [{I, C, W, _WC, K, T}|S#state.constraints]}). +-define(set_Constraints(S,C), S#state{constraints = C}). + +-define(del_AllTypes(S), ets:match_delete(S#state.etsR, {'_','_',types})). +-define(del_Type(S,I), ets:match_delete(S#state.etsR, {{I, '_'}, '_', types})). +-define(add_Type(S,I,ET,K), ets:insert(S#state.etsR, {{I, ET}, K, types})). + +%% Parse Tree +-define(add_ParseTree(S,K,T), ets:insert(S#state.etsR, {K, T, tree})). +-define(del_ParseTree(S,K), ets:delete(S#state.etsR, K)). +-define(del_AllParseTress(S), ets:match_delete(S#state.etsR, {'_','_',tree})). + +%%------------------- MISC ---------------------------------------- +-define(is_EmptyFilter(S), S#state.constraints==[]). + +-define(InfoSeq2EventTypeSeq(L), lists:flatten( + lists:map(fun(#'CosNotifyFilter_ConstraintInfo' + {constraint_expression= + #'CosNotifyFilter_ConstraintExp' + {event_types = ET}}) -> + ET + end, + L))). + +%%-----------------------------------------------------------% +%% Function : handle_info, code_change +%% Arguments: See gen_server documentation. +%% Effect : Functions demanded by the gen_server module. +%%------------------------------------------------------------ + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?debug_print("INFO: ~p DATA: ~p~n", [State, Info]), + case Info of + {'EXIT', _Pid, _Reason} -> + {noreply, State}; + _ -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% Function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([FiFac, FacPid, ConstraintGr]) -> + process_flag(trap_exit, true), + {ok, ?get_InitState(FiFac, FacPid, ConstraintGr)}. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%------- Exported external attributes ---------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Function : '_get_constraint_grammar'/2 +%% Type : readonly +%% Returns : Grammar - string() +%%----------------------------------------------------------- +'_get_constraint_grammar'(_OE_THIS, State) -> + {reply, ?get_Grammar(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Function : add_constraints/3 +%% Arguments: CL - CosNotifyFilter::ConstraintExpSeq +%% Returns : CosNotifyFilter::ConstraintInfoSeq | +%% {'EXCEPTION', CosNotifyFilter::InvalidConstraint} +%%----------------------------------------------------------- +add_constraints(_OE_THIS, State, CL) -> + {NewState, Filters, Info, EventTSeq} = try_create_filters(State, CL), + NewState2=store_filters(NewState, Filters), + inform_callbacks(?get_AllCallback(NewState2), EventTSeq, []), + {reply, Info, NewState2}. + +%%----------------------------------------------------------% +%% Function : modify_constraints/4 +%% Arguments: IDs - CosNotifyFilter::ConstraintIDSeq +%% AddConstraintInfoSeq - CosNotifyFilter::ConstraintInfoSeq +%% Returns : ok | +%% {'EXCEPTION', CosNotifyFilter::InvalidConstraint} | +%% {'EXCEPTION', CosNotifyFilter::ConstraintNotFound} +%%----------------------------------------------------------- +%% The OMG specification (TC Document telecom/98-11-01, chapter +%% 3.2.1.3) states (concerning IDs): +%% +%% "If all input values supplied within a particular invocation +%% of this operation are valid, then the specific constraints +%% identified by the values contained in the first input parameter +%% will be deleted from the list of those encapsulated by the target +%% filter object." +%% +%% Hence, first we must check if all ID's exists before deleting. +modify_constraints(_OE_THIS, State, IDs, AddConstraintInfoSeq) -> + %% The following functions are 'safe', i.e., they do not alter any data. + RemoveConstraintInfoSeq = lookup_constraints(IDs, State), + lookup_constraints(AddConstraintInfoSeq, State), + {NewState, Filters, _Info, AddedEventTSeq} = + try_create_filters(State, AddConstraintInfoSeq), + RemovedEventTSeq = ?InfoSeq2EventTypeSeq(RemoveConstraintInfoSeq), + + %% We cannot change anything before our checks (see above). Hence, + %% do NOT move the following lines above this point. + NewState2 = delete_constraints(IDs, NewState), + +%% The OMG specification (TC Document telecom/98-11-01, chapter +%% 3.2.1.3) states (concerning AddConstraintInfoSeq): +%% +%% "If all input values supplied within a particular invocation of this +%% operation are valid, then the constraint expression associated with the +%% already encapsulated constraint identified by the numeric value contained +%% within each element of the input sequence will be modified to the new +%% constraint expression that is contained within the same sequence element." +%% +%% This, our interpretation, implies that ALL previous data related +%% to each unique ID should be removed and replaced by the new data. + + NewState3 = delete_constraints(AddConstraintInfoSeq, NewState2), + NewState4 = store_filters(NewState3, Filters), + inform_callbacks(?get_AllCallback(NewState4), + AddedEventTSeq, RemovedEventTSeq), + {reply, ok, NewState4}. + +%%----------------------------------------------------------% +%% Function : get_constraints/3 +%% Arguments: IDs - CosNotifyFilter::ConstraintIDSeq +%% Returns : CosNotifyFilter::ConstraintInfoSeq | +%% {'EXCEPTION', CosNotifyFilter::ConstraintNotFound} +%%----------------------------------------------------------- +get_constraints(_OE_THIS, State, IDs) -> + {reply, lookup_constraints(IDs, State), State}. + +%%----------------------------------------------------------% +%% Function : get_all_constraints/2 +%% Arguments: - +%% Returns : CosNotifyFilter::ConstraintInfoSeq +%%----------------------------------------------------------- +get_all_constraints(_OE_THIS, State) -> + {reply, ?get_AllConstraints(State), State}. + +%%----------------------------------------------------------% +%% Function : remove_all_constraints/2 +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_constraints(_OE_THIS, State) -> + {reply, ok, ?del_AllConstraints(State)}. + +%%----------------------------------------------------------% +%% Function : destroy/2 +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +destroy(_OE_THIS, State) -> + {stop, normal, ok, State}. + +%%----------------------------------------------------------% +%% Function : match/3 +%% Arguments: Event - #any{} +%% Returns : boolean() | +%% {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData} +%%----------------------------------------------------------- + +match(_OE_THIS, State, Event) when is_record(Event,'any'), ?is_EmptyFilter(State) -> + {reply, true, State}; +match(_OE_THIS, State, Event) when is_record(Event,'any') -> + match_any_event(State, Event, ?get_ConstraintAllData(State)); +match(_,_,What) -> + orber:dbg("[~p] CosNotifyFilter_Filter:match(~p);~n" + "Not an CORBA::Any", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% Function : match_structured/3 +%% Arguments: Event - CosNotification::StructuredEvent +%% Returns : boolean() | +%% {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData} +%%----------------------------------------------------------- +match_structured(_OE_THIS, State, Event) when + is_record(Event,'CosNotification_StructuredEvent') andalso ?is_EmptyFilter(State) -> + {reply, true, State}; +match_structured(_OE_THIS, State, Event) when + is_record(Event,'CosNotification_StructuredEvent') -> + match_str_event(State, Event, ?get_ConstraintAllData(State)); +match_structured(_,_,What) -> + orber:dbg("[~p] CosNotifyFilter_Filter:match_structured(~p);~n" + "Not a StructuredEvent", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------* +%% Function : match_typed/3 +%% Arguments: Data - CosNotification::PropertySeq +%% Returns : boolean() | +%% {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData} +%%----------------------------------------------------------- +match_typed(_OE_THIS, _State, _Data) -> + corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% Function : attach_callback/3 +%% Arguments: CB - CosNotifyComm::NotifySubscribe +%% Returns : ID - CosNotifyFilter::CallbackID +%%----------------------------------------------------------- +attach_callback(_OE_THIS, State, CB) -> + 'CosNotification_Common':type_check(CB, 'CosNotifyComm_NotifySubscribe'), + CBID = ?new_Id(State), + {reply, CBID, ?add_Callback(State, CBID, CB)}. + +%%----------------------------------------------------------% +%% Function : detach_callback/3 +%% Arguments: ID - CosNotifyFilter::CallbackID +%% Returns : ok | {'EXCEPTION', CosNotifyFilter::CallbackNotFound} +%%----------------------------------------------------------- +detach_callback(_OE_THIS, State, ID) when is_integer(ID) -> + {reply, ok, ?del_Callback(State, ID)}; +detach_callback(_,_,What) -> + orber:dbg("[~p] CosNotifyFilter_Filter:detach_callback(~p);~n" + "Not an integer", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% Function : get_callbacks/2 +%% Arguments: - +%% Returns : CosNotifyFilter::CallbackIDSeq +%%----------------------------------------------------------- +get_callbacks(_OE_THIS, State) -> + {reply, ?get_AllCallbackID(State), State}. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +%% To match callbacks +find_obj({value, {_, Obj}},_) -> Obj; +%% To match constraints +find_obj({value, {Id, Con, _, _, _, Types}}, _) -> + ?create_ConstraintInfo(Types, Con, Id); +find_obj([{_, Tree, tree}|_], tree) -> Tree; +% Left out for now to avoid dialyzer warning. +%find_obj(_,callback) -> {'EXCEPTION', #'CosNotifyFilter_CallbackNotFound'{}}; +find_obj(_,tree) -> undefined; +find_obj(_,constraint) -> error. + +%% Delete a single object. +delete_obj(List,List,callback) -> corba:raise(#'CosNotifyFilter_CallbackNotFound'{}); +delete_obj(List,_,_) -> List. + +%% Delete given object from list and all related objects in DB (parse tree and types). +match_delete(State, Constraints, ID) -> + match_delete(State, Constraints, ID, []). +match_delete(_, [], _, _) -> + error; +match_delete(State, [{ID, _Con, _Which, _WC, Key, _Types}|T], ID, Acc) -> + ?del_Type(State, ID), + ?del_ParseTree(State, Key), + {ok, ?set_Constraints(State, Acc++T)}; +match_delete(State, [H|T], ID, Acc) -> + match_delete(State, T, ID, [H|Acc]). + +%% Remove all data related with constraints; for now, since no other data +%% stored in DB, we do in a rather brutal way. +clear_DB(State) -> + catch ets:delete(State#state.etsR), + State#state{etsR = ets:new(oe_ets, [bag, protected]), constraints=[]}. + +%% Given a list of Constrain IDs we want to find the related constraints. +%% !!!!!! This function may not alter any data in DB in any way !!!!!!!!!! +lookup_constraints(IDs, State) -> + lookup_constraints(IDs, State, []). +lookup_constraints([], _State, Accum) -> + Accum; +lookup_constraints([H|T], State, Accum) + when is_record(H, 'CosNotifyFilter_ConstraintInfo') -> + case ?get_Constraint(State, H#'CosNotifyFilter_ConstraintInfo'.constraint_id) of + error -> + corba:raise(#'CosNotifyFilter_ConstraintNotFound' + {id = H#'CosNotifyFilter_ConstraintInfo'.constraint_id}); + _Con -> + %% We don't need to collect the result since the input already is of + %% the correct type, i.e., ConstraintInfoSeq + lookup_constraints(T, State, Accum) + end; +lookup_constraints([H|T], State, Accum) when is_integer(H) -> + case ?get_Constraint(State,H) of + error -> + corba:raise(#'CosNotifyFilter_ConstraintNotFound'{id=H}); + Con -> + lookup_constraints(T, State, [Con|Accum]) + end; +lookup_constraints(_, _, _) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%% Given a list of Constrain IDs we want to delet the related constraints. +%% We need also to return the ConstraintInfoSeq described related to the +%% given ID's. +delete_constraints([], State) -> + State; +delete_constraints([H|T], State) + when is_record(H, 'CosNotifyFilter_ConstraintInfo') -> + case catch ?del_Constraint(State, + H#'CosNotifyFilter_ConstraintInfo'.constraint_id) of + {ok, NewState} -> + delete_constraints(T, NewState); + Reason -> + orber:dbg("[~p] 'CosNotifyFilter_Filter':modify_constraints().~n" + "Unable to remove: ~p~n" + "Reason: ~p~n", + [?LINE, H, Reason], ?DEBUG_LEVEL), + delete_constraints(T, State) + end; +delete_constraints([H|T], State) -> + case catch ?del_Constraint(State,H) of + {ok, NewState} -> + delete_constraints(T, NewState); + Reason -> + orber:dbg("[~p] 'CosNotifyFilter_Filter':modify_constraints().~n" + "Unable to remove: ~p~n" + "Reason: ~p~n", + [?LINE, H, Reason], ?DEBUG_LEVEL), + delete_constraints(T, State) + end. + +%% Inform all registered callbacks that the constraints have changed. +%% Added and Removed must be a CosNotification::EventTypeSeq +inform_callbacks([],_,_) -> + ok; +inform_callbacks([H|T], Added, Removed) -> + case catch 'CosNotifyComm_NotifySubscribe':subscription_change(H, Added, Removed) of + ok -> + ?debug_print("INFORMED CALLBACK: ~p ADDED: ~p REMOVED: ~p", + [H, Added, Removed]), + inform_callbacks(T, Added, Removed); + Other -> + orber:dbg("[~p] 'CosNotifyComm_NotifySubscribe':subscription_change().~n" + "Unable to inform callback: ~p~n" + "Reason: ~p~n", + [?LINE, H, Other], ?DEBUG_LEVEL), + inform_callbacks(T, Added, Removed) + end. + +%%----------------------------------------------------------- +%% Function : try_create_filters/2 +%% Arguments: CL - #'CosNotifyFilter_ConstraintExp'{ +%% event_types = [#'CosNotification_EventType'{ +%% domain_name = Str, type_name = Str}] +%% constraint_expr = Str} +%% Returns : {State, AccumList} +%%----------------------------------------------------------- +%% !!!!!! This function may not alter any data in DB in any way !!!!!!!!!! +try_create_filters(State, CL) -> + try_create_filters(State, CL, [], [], []). +try_create_filters(State, [], Accum, InfoSeq, EventTSeq) -> + {State, Accum, InfoSeq, EventTSeq}; +try_create_filters(State, [#'CosNotifyFilter_ConstraintExp'{event_types = Types, + constraint_expr = Con}|T], + Accum, InfoSeq, EventTSeq) -> + case catch cosNotification_Filter:create_filter(Con) of + {ok, Tree} -> + case catch cosNotification_Filter:check_types(Types) of + true -> + ID = ?new_Id(State), + Key = ?not_CreateDBKey, + NewETSeq = Types ++ EventTSeq, + try_create_filters(?set_IdCounter(State, ID), T, + [{ID, true, [], Key, Types, Con, Tree}|Accum], + [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], + NewETSeq); + {ok, Which, WC} -> + ID = ?new_Id(State), + Key = ?not_CreateDBKey, + NewETSeq = Types ++ EventTSeq, + try_create_filters(?set_IdCounter(State, ID), T, + [{ID, Which, WC, Key, Types, Con, Tree}|Accum], + [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], + NewETSeq); + _ -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; + _ -> + corba:raise(#'CosNotifyFilter_InvalidConstraint' + {constr = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}}) + end; +try_create_filters(State, [#'CosNotifyFilter_ConstraintInfo' + {constraint_expression = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}, + constraint_id=ID}|T], Accum, InfoSeq, EventTSeq) -> + case catch cosNotification_Filter:create_filter(Con) of + {ok, Tree} -> + case catch cosNotification_Filter:check_types(Types) of + true -> + Key = ?not_CreateDBKey, + NewETSeq = Types ++ EventTSeq, + try_create_filters(State, T, + [{ID, true, [], Key, Types, Con, Tree}|Accum], + [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], + NewETSeq); + {ok, Which, WC} -> + Key = ?not_CreateDBKey, + NewETSeq = Types ++ EventTSeq, + try_create_filters(State, T, + [{ID, Which, WC, Key, Types, Con, Tree}|Accum], + [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], + NewETSeq); + _ -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; + _ -> + corba:raise(#'CosNotifyFilter_InvalidConstraint' + {constr = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}}) + end; +try_create_filters(_,_,_,_,_) -> + %% The list contained something else but ConstraintExp. + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------- +%% Function : store_filters/4 +%% Arguments: Filters - a list of filters. +%% Returns : +%%----------------------------------------------------------- + +store_filters(State, []) -> + State; +store_filters(State, [{ID, Which, WC, Key, Types, Con, Tree}|T]) -> + ?add_ParseTree(State, Key, Tree), + write_types(State, Types, ID, Key), + store_filters(?add_Constraint(State, ID, Con, Which, WC, Key, Types), T). + + +write_types(_State, [],_, _) -> + ok; +write_types(State, [EventType|T], ID, Key) -> + ?add_Type(State, ID, EventType, Key), + ?debug_print("FILTER: ~p ~p ~p~n", [ID, Key, EventType]), + write_types(State, T, ID, Key). + +%%----------------------------------------------------------- +%% Function : match_any_event +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +match_any_event(State, _Event, []) -> + ?debug_print("FILTER REJECTED: ~p~n", [_Event]), + {reply, false, State}; +match_any_event(State, Event, [{_, _, _, _, Key, _}|T]) -> + case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), Event) of + true -> + ?debug_print("FILTER APPROVED (WC): ~p~n", [Event]), + {reply, true, State}; + _ -> + match_any_event(State, Event, T) + end. + + +%%----------------------------------------------------------- +%% Function : match_str_event +%% Arguments: +%% Returns : +%%----------------------------------------------------------- + +match_str_event(State, _Event, []) -> + ?debug_print("FILTER REJECTED: ~p~n", [_Event]), + {reply, false, State}; +match_str_event(State, Event, [{ID, _Con, Which, WC, Key, _Types}|T]) -> + ET = ((Event#'CosNotification_StructuredEvent'.header) + #'CosNotification_EventHeader'.fixed_header) + #'CosNotification_FixedEventHeader'.event_type, + CheckList = + case Which of + both -> + [ET]; + domain -> + [ET, + ET#'CosNotification_EventType'{type_name=""}, + ET#'CosNotification_EventType'{type_name="*"}]; + type -> + [ET, + ET#'CosNotification_EventType'{domain_name=""}, + ET#'CosNotification_EventType'{domain_name="*"}]; + _ -> + [ET, + ET#'CosNotification_EventType'{type_name=""}, + ET#'CosNotification_EventType'{type_name="*"}, + ET#'CosNotification_EventType'{domain_name=""}, + ET#'CosNotification_EventType'{domain_name="*"}] + end, + case check_DB(State, ID, CheckList) of + false -> + %% No match, may have used wildcards, e.g., "dom*". + case catch cosNotification_Filter:match_types( + ET#'CosNotification_EventType'.domain_name, + ET#'CosNotification_EventType'.type_name, + WC) of + true -> + case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), + Event) of + true -> + ?debug_print("FILTER APPROVED (WC): ~p~n", [Event]), + {reply, true, State}; + _ -> + match_str_event(State, Event, T) + end; + _-> + match_str_event(State, Event, T) + end; + Key -> + case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), + Event) of + true -> + ?debug_print("FILTER APPROVED: ~p~n", [Event]), + {reply, true, State}; + _ -> + match_str_event(State, Event, T) + end + end. + +check_DB(_, _, []) -> + false; +check_DB(State, ID, [H|T]) -> + case ?match_Type(State, ID, H) of + [] -> + check_DB(State, ID, T); + [{_, K, types}|_] -> + K + end. + + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ + diff --git a/lib/cosNotification/src/CosNotifyFilter_MappingFilter_impl.erl b/lib/cosNotification/src/CosNotifyFilter_MappingFilter_impl.erl new file mode 100644 index 0000000000..f9103001f1 --- /dev/null +++ b/lib/cosNotification/src/CosNotifyFilter_MappingFilter_impl.erl @@ -0,0 +1,578 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : CosNotifyFilter_MappingFilter_impl.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module('CosNotifyFilter_MappingFilter_impl'). + +%%--------------- INCLUDES ----------------------------------- +%% Application files +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- IMPORTS ------------------------------------ + +%%--------------- EXPORTS ------------------------------------ +%% External Attributes +-export(['_get_constraint_grammar'/2, + '_get_value_type'/2, + '_get_default_value'/2]). +%% External Functions +-export([add_mapping_constraints/3, + modify_mapping_constraints/4, + get_mapping_constraints/3, + get_all_mapping_constraints/2, + remove_all_mapping_constraints/2, + destroy/2, + match/3, + match_structured/3, + match_typed/3]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%%######### MISC ########## +-define(create_MappingInfo(_Types, _Con, _ID, _A), + #'CosNotifyFilter_MappingConstraintInfo'{ + constraint_expression = + #'CosNotifyFilter_ConstraintExp'{ + event_types = _Types, + constraint_expr = _Con}, + constraint_id = _ID, + value = _A + }). +%%#### Data structures #### +-record(state, {constraint_grammar, + value, + typeC, + constraints = [], + filters = [], + idCounter = 0, + filterFactory, + factoryPid, + etsR}). + +%% Data structures constructors +-define(get_InitState(Gr, DVal, FF, FP), + #state{constraint_grammar=Gr, + value = DVal, + typeC = any:get_typecode(DVal), + filterFactory = FF, + factoryPid = FP, + etsR = ets:new(oe_ets, [bag, protected])}). + +%%------------------- Data structures selectors ------------------- +%% Attributes +-define(get_Grammar(S), S#state.constraint_grammar). +-define(get_DefVal(S), S#state.value). +-define(get_DefTC(S), S#state.typeC). +-define(get_DefAny(S), S#state.value). + +%% ID:s +-define(get_IdCounter(S), S#state.idCounter). + +%% Constraints +-define(get_Constraint(S,I), find_obj(lists:keysearch(I, 1, S#state.constraints), + constraint)). +-define(get_AllConstraints(S), lists:map(fun({I, C, _W, _WC, _K, T, A}) -> + ?create_MappingInfo(T, C, I, A) + end, + S#state.constraints)). +-define(get_ConstraintAllData(S), S#state.constraints). +-define(get_ConstraintData(S,I), lists:keysearch(I, 1, S#state.constraints)). +-define(match_Type(S,I,ET), ets:lookup(S#state.etsR, {I, ET})). +%% Parse Tree +-define(get_ParseTree(S,K), find_obj(ets:lookup(S#state.etsR, K), tree)). + +%%------------------- Data structures modifiers ------------------- +%% ID:s +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). + +%% Constraints +-define(del_Constraint(S, I), match_delete(S, S#state.constraints, I)). +-define(del_AllConstraints(S), clear_DB(S)). +-define(add_Constraint(S,I,C,W,_WC,K,T,A), S#state{constraints = + [{I, C, W, _WC, K, T, A}|S#state.constraints]}). +-define(set_Constraints(S,C), S#state{constraints = C}). + +-define(del_AllTypes(S), ets:match_delete(S#state.etsR, {'_','_',types})). +-define(del_Type(S,I), ets:match_delete(S#state.etsR, {{I, '_'}, '_', types})). +-define(add_Type(S,I,ET,K), ets:insert(S#state.etsR, {{I, ET}, K, types})). + +%% Parse Tree +-define(add_ParseTree(S,K,T), ets:insert(S#state.etsR, {K, T, tree})). +-define(del_ParseTree(S,K), ets:delete(S#state.etsR, K)). +-define(del_AllParseTress(S), ets:match_delete(S#state.etsR, {'_','_',tree})). + +%%------------------- MISC ---------------------------------------- +-define(is_EmptyFilter(S), S#state.constraints==[]). +-define(is_EqualType(S,T), S#state.typeC==any:get_typecode(T)). + +%%-----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: See gen_server documentation. +%% Effect : Functions demanded by the gen_server module. +%%------------------------------------------------------------ + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?debug_print("INFO: ~p DATA: ~p~n", [State, Info]), + case Info of + {'EXIT', _Pid, _Reason} -> + {noreply, State}; + _ -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([FiFac, FacPid, InitGr, DefVal]) -> + process_flag(trap_exit, true), + {ok, ?get_InitState(InitGr, DefVal, FiFac, FacPid)}. + +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------- +%%------- Exported external attributes ---------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Function : '_get_constraint_grammar'/2 +%% Type : readonly +%% Returns : string() +%%----------------------------------------------------------- +'_get_constraint_grammar'(_OE_THIS, State) -> + {reply, ?get_Grammar(State), State}. +%%----------------------------------------------------------% +%% Function : '_get_value_type'/2 +%% Type : readonly +%% Returns : CORBA::TypeCode +%%----------------------------------------------------------- +'_get_value_type'(_OE_THIS, State) -> + {reply, ?get_DefTC(State), State}. +%%----------------------------------------------------------% +%% Function : '_get_default_value'/2 +%% Type : readonly +%% Returns : #any{} +%%----------------------------------------------------------- +'_get_default_value'(_OE_THIS, State) -> + {reply, ?get_DefVal(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Function : add_mapping_constraints/3 +%% Arguments: Pairs - CosNotifyFilter::MappingConstraintPairSeq +%% Returns : CosNotifyFilter::MappingConstraintInfoSeq | +%% {'EXCEPTION', CosNotifyFilter::InvalidConstraint} | +%% {'EXCEPTION', CosNotifyFilter::InvalidValue} +%%----------------------------------------------------------- +add_mapping_constraints(_OE_THIS, State, Pairs) -> + {NewState, Filters, Info} = try_create_filters(State, Pairs), + NewState2=store_filters(NewState, Filters), + {reply, Info, NewState2}. + +%%----------------------------------------------------------% +%% Function : modify_mapping_constraints/4 +%% Arguments: IDs - CosNotifyFilter::ConstraintIDSeq +%% Info - CosNotifyFilter::MappingConstraintInfoSeq +%% Returns : ok | +%% {'EXCEPTION', CosNotifyFilter::InvalidConstraint} | +%% {'EXCEPTION', CosNotifyFilter::InvalidValue} | +%% {'EXCEPTION', CosNotifyFilter::ConstraintNotFound} +%%----------------------------------------------------------- +modify_mapping_constraints(_OE_THIS, State, IDs, InfoSeq) -> + lookup_constraints(IDs, State), + lookup_constraints(InfoSeq, State), + {NewState, Filters, _Info} = try_create_filters(State, InfoSeq), + + %% We cannot change anything before our checks (see above). Hence, + %% do NOT move the following lines above this point. + + NewState2 = delete_constraints(IDs, NewState), + NewState3 = delete_constraints(InfoSeq, NewState2), + NewState4 = store_filters(NewState3, Filters), + {reply, ok, NewState4}. + +%%----------------------------------------------------------% +%% Function : get_mapping_constraints/3 +%% Arguments: IDs - CosNotifyFilter::ConstraintIDSeq +%% Returns : CosNotifyFilter::MappingConstraintInfoSeq | +%% {'EXCEPTION', CosNotifyFilter::ConstraintNotFound} +%%----------------------------------------------------------- +get_mapping_constraints(_OE_THIS, State, IDs) -> + {reply, lookup_constraints(IDs, State), State}. + +%%----------------------------------------------------------% +%% Function : get_all_mapping_constraints/2 +%% Arguments: - +%% Returns : CosNotifyFilter::MappingConstraintInfoSeq +%%----------------------------------------------------------- +get_all_mapping_constraints(_OE_THIS, State) -> + {reply, ?get_AllConstraints(State), State}. + +%%----------------------------------------------------------% +%% Function : remove_all_mapping_constraints/2 +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_mapping_constraints(_OE_THIS, State) -> + {reply, ok, ?del_AllConstraints(State)}. + +%%----------------------------------------------------------% +%% Function : destroy/2 +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +destroy(_OE_THIS, State) -> + {stop, normal, ok, State}. + +%%----------------------------------------------------------% +%% Function : match/3 +%% Arguments: Event - #any{} +%% Returns : boolean(), #any{} (out-type) | +%% {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData} +%%----------------------------------------------------------- +match(_OE_THIS, State, Event) when is_record(Event,'any') andalso ?is_EmptyFilter(State) -> + {reply, {false, ?get_DefAny(State)}, State}; +match(_OE_THIS, State, Event) when is_record(Event,'any') -> + match_any_event(State, Event, ?get_ConstraintAllData(State)); +match(_,_,_) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + + +%%----------------------------------------------------------% +%% Function : match_structured/3 +%% Arguments: Event - CosNotification::StructuredEvent +%% Returns : boolean(), #any{} (out-type) | +%% {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData} +%%----------------------------------------------------------- +match_structured(_OE_THIS, State, Event) when + is_record(Event,'CosNotification_StructuredEvent') andalso ?is_EmptyFilter(State) -> + {reply, {false, ?get_DefAny(State)}, State}; +match_structured(_OE_THIS, State, Event) when + is_record(Event,'CosNotification_StructuredEvent') -> + match_str_event(State, Event, ?get_ConstraintAllData(State)); +match_structured(_,_,_) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------* +%% Function : match_typed/3 +%% Arguments: Data - CosNotification::PropertySeq +%% Returns : boolean() , #any{} (out-type) | +%% {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData} +%%----------------------------------------------------------- +match_typed(_OE_THIS, _State, _Data) -> + corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_NO}). + +%%--------------- LOCAL FUNCTIONS ---------------------------- +%% To match constraints +find_obj({value, {Id, Con, _, _, _, Types, Any}}, _) -> + ?create_MappingInfo(Types, Con, Id, Any); +find_obj([{_, Tree, tree}|_], tree) -> Tree; +find_obj(_,tree) -> undefined; +find_obj(_,constraint) -> error. + +%% Delete given object from list and all related objects in DB (parse tree and types). +match_delete(State, Constraints, ID) -> + match_delete(State, Constraints, ID, []). +match_delete(_, [], _, _) -> + error; +match_delete(State, [{ID, _Con, _Which, _WC, Key, _Types, _Any}|T], ID, Acc) -> + ?del_Type(State, ID), + ?del_ParseTree(State, Key), + {ok, ?set_Constraints(State, Acc++T)}; +match_delete(State, [H|T], ID, Acc) -> + match_delete(State, T, ID, [H|Acc]). + +%% Remove all data related with constraints; for now, since no other data +%% stored in DB, we do in a rather brutal way. +clear_DB(State) -> + catch ets:delete(State#state.etsR), + State#state{etsR = ets:new(oe_ets, [bag, protected]), constraints=[]}. + +%% Given a list of Constrain IDs we want to find the related constraints. +%% !!!!!! This function may not alter any data in DB in any way !!!!!!!!!! +lookup_constraints(IDs, State) -> + lookup_constraints(IDs, State, []). +lookup_constraints([], _State, Accum) -> + Accum; +lookup_constraints([H|T], State, Accum) + when is_record(H, 'CosNotifyFilter_MappingConstraintInfo') -> + case ?get_Constraint(State, H#'CosNotifyFilter_MappingConstraintInfo'.constraint_id) of + error -> + corba:raise(#'CosNotifyFilter_ConstraintNotFound' + {id = H#'CosNotifyFilter_MappingConstraintInfo'.constraint_id}); + _Con -> + %% We don't need to collect the result since the input already is of + %% the correct type, i.e., ConstraintInfoSeq + lookup_constraints(T, State, Accum) + end; +lookup_constraints([H|T], State, Accum) when is_integer(H) -> + case ?get_Constraint(State,H) of + error -> + corba:raise(#'CosNotifyFilter_ConstraintNotFound'{id=H}); + Con -> + lookup_constraints(T, State, [Con|Accum]) + end; +lookup_constraints(_, _, _) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%% Given a list of Constrain IDs we want to delet the related constraints. +%% We need also to return the ConstraintInfoSeq described related to the +%% given ID's. +delete_constraints([], State) -> + State; +delete_constraints([H|T], State) + when is_record(H, 'CosNotifyFilter_MappingConstraintInfo') -> + case catch ?del_Constraint(State, + H#'CosNotifyFilter_MappingConstraintInfo'.constraint_id) of + {ok, NewState} -> + delete_constraints(T, NewState); + Reason -> + orber:dbg("[~p] 'CosNotifyFilter_MappingFilter':modify_mapping_constraints().~n" + "Unable to remove: ~p~n" + "Reason: ~p~n", + [?LINE, H, Reason], ?DEBUG_LEVEL), + delete_constraints(T, State) + end; +delete_constraints([H|T], State) -> + case catch ?del_Constraint(State,H) of + {ok, NewState} -> + delete_constraints(T, NewState); + Reason -> + orber:dbg("[~p] 'CosNotifyFilter_MappingFilter':modify_mapping_constraints().~n" + "Unable to remove: ~p~n" + "Reason: ~p~n", + [?LINE, H, Reason], ?DEBUG_LEVEL), + delete_constraints(T, State) + end. + +%%----------------------------------------------------------- +%% Function : try_create_filters/2 +%% Arguments: CL - #'CosNotifyFilter_MappingConstraintPair{ +%% constraint_expression = +%% #'CosNotifyFilter_ConstraintExp'{ +%% event_types = +%% [#'CosNotification_EventType'{ +%% domain_name = Str, type_name = Str}] +%% constraint_expr = Str}, +%% result_to_set = Any} +%% Returns : {State, AccumList} +%%----------------------------------------------------------- +%% !!!!!! This function may not alter any data in DB in any way !!!!!!!!!! +try_create_filters(State, CL) -> + try_create_filters(State, CL, [], []). +try_create_filters(State, [], Accum, InfoSeq) -> + {State, Accum, InfoSeq}; +try_create_filters(State, [#'CosNotifyFilter_MappingConstraintPair' + {constraint_expression = + #'CosNotifyFilter_ConstraintExp'{event_types = Types, + constraint_expr = Con}, + result_to_set=Any}|T], Accum, InfoSeq) -> + case catch {?is_EqualType(State,Any), cosNotification_Filter:create_filter(Con)} of + {false, _} -> + corba:raise(#'CosNotifyFilter_InvalidValue' + {constr = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}, + value=Any}); + {_, {ok, Tree}} -> + case catch cosNotification_Filter:check_types(Types) of + true -> + ID = ?new_Id(State), + Key = ?not_CreateDBKey, + try_create_filters(?set_IdCounter(State, ID), T, + [{ID, true, [], Key, Types, Con, Tree, Any}|Accum], + [?create_MappingInfo(Types, Con, ID, Any)|InfoSeq]); + {ok, Which, WC} -> + ID = ?new_Id(State), + Key = ?not_CreateDBKey, + try_create_filters(?set_IdCounter(State, ID), T, + [{ID, Which, WC, Key, Types, Con, Tree, Any}|Accum], + [?create_MappingInfo(Types, Con, ID, Any)|InfoSeq]); + _ -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; + _ -> + corba:raise(#'CosNotifyFilter_InvalidConstraint' + {constr = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}}) + end; +try_create_filters(State, [#'CosNotifyFilter_MappingConstraintInfo' + {constraint_expression = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}, + constraint_id=ID, + value=Any}|T], Accum, InfoSeq) -> + case catch cosNotification_Filter:create_filter(Con) of + {ok, Tree} -> + case catch cosNotification_Filter:check_types(Types) of + true -> + Key = ?not_CreateDBKey, + try_create_filters(State, T, + [{ID, true, [], Key, Types, Con, Tree, Any}|Accum], + [?create_MappingInfo(Types, Con, ID, Any)|InfoSeq]); + {ok, Which, WC} -> + Key = ?not_CreateDBKey, + try_create_filters(State, T, + [{ID, Which, WC, Key, Types, Con, Tree, Any}|Accum], + [?create_MappingInfo(Types, Con, ID, Any)|InfoSeq]); + _ -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; + _ -> + corba:raise(#'CosNotifyFilter_InvalidConstraint' + {constr = #'CosNotifyFilter_ConstraintExp' + {event_types = Types, constraint_expr = Con}}) + end; +try_create_filters(_,_,_,_) -> + %% The list contained something else but ConstraintExp. + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------- +%% Function : store_filters/4 +%% Arguments: Filters - a list of filters. +%% Returns : +%%----------------------------------------------------------- + +store_filters(State, []) -> + State; +store_filters(State, [{ID, Which, WC, Key, Types, Con, Tree, Any}|T]) -> + ?add_ParseTree(State, Key, Tree), + write_types(State, Types, ID, Key), + store_filters(?add_Constraint(State, ID, Con, Which, WC, Key, Types, Any), T). + + +write_types(_State, [],_, _) -> + ok; +write_types(State, [EventType|T], ID, Key) -> + ?add_Type(State, ID, EventType, Key), + ?debug_print("FILTER: ~p ~p ~p~n", [ID, Key, EventType]), + write_types(State, T, ID, Key). + +%%----------------------------------------------------------- +%% Function : match_any_event +%% Arguments: Event - #any{} +%% Returns : +%%----------------------------------------------------------- +match_any_event(State, _Event, []) -> + ?debug_print("FILTER REJECTED: ~p~n", [_Event]), + {reply, {false, ?get_DefAny(State)}, State}; +match_any_event(State, Event, [{_, _, _, _, Key, Any}|T]) -> + case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), Event) of + true -> + ?debug_print("FILTER APPROVED (WC): ~p~n", [Event]), + {reply, {true, Any}, State}; + _ -> + match_any_event(State, Event, T) + end. + + +%%----------------------------------------------------------- +%% Function : match_str_event +%% Arguments: +%% Returns : +%%----------------------------------------------------------- + +match_str_event(State, _Event, []) -> + ?debug_print("FILTER REJECTED: ~p~n", [_Event]), + {reply, {false, ?get_DefAny(State)}, State}; +match_str_event(State, Event, [{ID, _Con, Which, WC, Key, _Types, Any}|T]) -> + ET = ((Event#'CosNotification_StructuredEvent'.header) + #'CosNotification_EventHeader'.fixed_header) + #'CosNotification_FixedEventHeader'.event_type, + CheckList = + case Which of + both -> + [ET]; + domain -> + [ET, + ET#'CosNotification_EventType'{type_name=""}, + ET#'CosNotification_EventType'{type_name="*"}]; + type -> + [ET, + ET#'CosNotification_EventType'{domain_name=""}, + ET#'CosNotification_EventType'{domain_name="*"}]; + _ -> + [ET, + ET#'CosNotification_EventType'{type_name=""}, + ET#'CosNotification_EventType'{type_name="*"}, + ET#'CosNotification_EventType'{domain_name=""}, + ET#'CosNotification_EventType'{domain_name="*"}] + end, + case check_DB(State, ID, CheckList) of + false -> + %% No match, may have used wildcards, e.g., "dom*". + case catch cosNotification_Filter:match_types( + ET#'CosNotification_EventType'.domain_name, + ET#'CosNotification_EventType'.type_name, + WC) of + true -> + case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), + Event) of + true -> + ?debug_print("FILTER APPROVED (WC): ~p~n", [Event]), + {reply, {true, Any}, State}; + _ -> + match_str_event(State, Event, T) + end; + _-> + match_str_event(State, Event, T) + end; + Key -> + case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), + Event) of + true -> + ?debug_print("FILTER APPROVED: ~p~n", [Event]), + {reply, {true, Any}, State}; + _ -> + match_str_event(State, Event, T) + end + end. + +check_DB(_, _, []) -> + false; +check_DB(State, ID, [H|T]) -> + case ?match_Type(State, ID, H) of + [] -> + check_DB(State, ID, T); + [{_, K, types}|_] -> + K + end. +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ + diff --git a/lib/cosNotification/src/CosTypedEvent.idl b/lib/cosNotification/src/CosTypedEvent.idl new file mode 100644 index 0000000000..d77c37731a --- /dev/null +++ b/lib/cosNotification/src/CosTypedEvent.idl @@ -0,0 +1,57 @@ +#ifndef _COS_TYPED_EVENT_IDL_ +#define _COS_TYPED_EVENT_IDL_ + +#pragma prefix "omg.org" + +#include<CosEvent.idl> + +module CosTypedEventComm { + + interface TypedPushConsumer : CosEventComm::PushConsumer { + Object get_typed_consumer(); + }; + + interface TypedPullSupplier : CosEventComm::PullSupplier { + Object get_typed_supplier(); + }; +}; + +module CosTypedEventChannelAdmin { + + exception InterfaceNotSupported {}; + exception NoSuchImplementation {}; + typedef string Key; + + + interface TypedProxyPushConsumer : + CosEventChannelAdmin::ProxyPushConsumer, + CosTypedEventComm::TypedPushConsumer { }; + + interface TypedProxyPullSupplier : + CosEventChannelAdmin::ProxyPullSupplier, + CosTypedEventComm::TypedPullSupplier { }; + + interface TypedSupplierAdmin : + CosEventChannelAdmin::SupplierAdmin { + TypedProxyPushConsumer obtain_typed_push_consumer(in Key supported_interface) + raises(InterfaceNotSupported); + CosEventChannelAdmin::ProxyPullConsumer obtain_typed_pull_consumer (in Key uses_interface) + raises(NoSuchImplementation); + }; + + interface TypedConsumerAdmin : + CosEventChannelAdmin::ConsumerAdmin { + TypedProxyPullSupplier obtain_typed_pull_supplier(in Key supported_interface) + raises (InterfaceNotSupported); + CosEventChannelAdmin::ProxyPushSupplier obtain_typed_push_supplier(in Key uses_interface) + raises(NoSuchImplementation); + }; + + interface TypedEventChannel { + TypedConsumerAdmin for_consumers(); + TypedSupplierAdmin for_suppliers(); + void destroy (); + }; +}; + +#endif /* ifndef _COS_TYPED_EVENT_IDL_ */ diff --git a/lib/cosNotification/src/CosTypedNotification.idl b/lib/cosNotification/src/CosTypedNotification.idl new file mode 100644 index 0000000000..882cde16e0 --- /dev/null +++ b/lib/cosNotification/src/CosTypedNotification.idl @@ -0,0 +1,109 @@ +#ifndef _COS_TYPED_NOTIFICATION_IDL_ +#define _COS_TYPED_NOTIFICATION_IDL_ + +#pragma prefix "omg.org" + +#include<CosNotifyChannelAdmin.idl> +#include<CosTypedEvent.idl> +#include<CosNotification.idl> + +module CosTypedNotifyComm { + interface TypedPushConsumer : CosTypedEventComm::TypedPushConsumer, CosNotifyComm::NotifyPublish { }; // TypedPushConsumer + + interface TypedPullSupplier : CosTypedEventComm::TypedPullSupplier, CosNotifyComm::NotifySubscribe { }; // TypedPullSupplier +}; // CosTypedNotifyComm + + +module CosTypedNotifyChannelAdmin { + // Forward declaration + interface TypedEventChannelFactory; + typedef string Key; + interface TypedProxyPushConsumer : CosNotifyChannelAdmin::ProxyConsumer, CosTypedNotifyComm::TypedPushConsumer { + void connect_typed_push_supplier (in CosEventComm::PushSupplier push_supplier) + raises (CosEventChannelAdmin::AlreadyConnected); + }; // TypedProxyPushConsumer + + interface TypedProxyPullSupplier : CosNotifyChannelAdmin::ProxySupplier, CosTypedNotifyComm::TypedPullSupplier { + void connect_typed_pull_consumer (in CosEventComm::PullConsumer pull_consumer) + raises (CosEventChannelAdmin::AlreadyConnected); + }; // TypedProxyPullSupplier + + interface TypedProxyPullConsumer : CosNotifyChannelAdmin::ProxyConsumer, CosNotifyComm::PullConsumer { + void connect_typed_pull_supplier (in CosTypedEventComm::TypedPullSupplier pull_supplier) + raises (CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises (CosNotifyChannelAdmin::ConnectionAlreadyInactive, CosNotifyChannelAdmin::NotConnected); + + void resume_connection() + raises (CosNotifyChannelAdmin::ConnectionAlreadyActive, CosNotifyChannelAdmin::NotConnected); + }; // TypedProxyPullConsumer + + interface TypedProxyPushSupplier : CosNotifyChannelAdmin::ProxySupplier, CosNotifyComm::PushSupplier { + void connect_typed_push_consumer (in CosTypedEventComm::TypedPushConsumer push_consumer) + raises (CosEventChannelAdmin::AlreadyConnected, CosEventChannelAdmin::TypeError); + + void suspend_connection() + raises (CosNotifyChannelAdmin::ConnectionAlreadyInactive, CosNotifyChannelAdmin::NotConnected); + + void resume_connection() + raises (CosNotifyChannelAdmin::ConnectionAlreadyActive, CosNotifyChannelAdmin::NotConnected); + }; // TypedProxyPushSupplier + + interface TypedConsumerAdmin : CosNotifyChannelAdmin::ConsumerAdmin, CosTypedEventChannelAdmin::TypedConsumerAdmin { + TypedProxyPullSupplier obtain_typed_notification_pull_supplier(in Key supported_interface, + out CosNotifyChannelAdmin::ProxyID proxy_id) + raises( CosTypedEventChannelAdmin::InterfaceNotSupported, CosNotifyChannelAdmin::AdminLimitExceeded ); + + TypedProxyPushSupplier obtain_typed_notification_push_supplier(in Key uses_interface, + out CosNotifyChannelAdmin::ProxyID proxy_id) + raises(CosTypedEventChannelAdmin::NoSuchImplementation, CosNotifyChannelAdmin::AdminLimitExceeded); + }; // TypedConsumerAdmin + + interface TypedSupplierAdmin : CosNotifyChannelAdmin::SupplierAdmin, CosTypedEventChannelAdmin::TypedSupplierAdmin { + TypedProxyPushConsumer obtain_typed_notification_push_consumer(in Key supported_interface, + out CosNotifyChannelAdmin::ProxyID proxy_id) + raises( CosTypedEventChannelAdmin::InterfaceNotSupported, CosNotifyChannelAdmin::AdminLimitExceeded); + TypedProxyPullConsumer obtain_typed_notification_pull_consumer(in Key uses_interface, + out CosNotifyChannelAdmin::ProxyID proxy_id ) + raises(CosTypedEventChannelAdmin::NoSuchImplementation, CosNotifyChannelAdmin::AdminLimitExceeded); + }; // TypedSupplierAdmin + + interface TypedEventChannel : CosNotification::QoSAdmin, CosNotification::AdminPropertiesAdmin, + CosTypedEventChannelAdmin::TypedEventChannel { + readonly attribute TypedEventChannelFactory MyFactory; + readonly attribute TypedConsumerAdmin default_consumer_admin; + readonly attribute TypedSupplierAdmin default_supplier_admin; + readonly attribute CosNotifyFilter::FilterFactory default_filter_factory; + + TypedConsumerAdmin new_for_typed_notification_consumers(in CosNotifyChannelAdmin::InterFilterGroupOperator op, + out CosNotifyChannelAdmin::AdminID id); + + TypedSupplierAdmin new_for_typed_notification_suppliers(in CosNotifyChannelAdmin::InterFilterGroupOperator op, + out CosNotifyChannelAdmin::AdminID id); + + TypedConsumerAdmin get_consumeradmin (in CosNotifyChannelAdmin::AdminID id ) + raises (CosNotifyChannelAdmin::AdminNotFound); + + TypedSupplierAdmin get_supplieradmin (in CosNotifyChannelAdmin::AdminID id) + raises (CosNotifyChannelAdmin::AdminNotFound); + + CosNotifyChannelAdmin::AdminIDSeq get_all_consumeradmins(); + + CosNotifyChannelAdmin::AdminIDSeq get_all_supplieradmins(); + }; // TypedEventChannel + + interface TypedEventChannelFactory { + TypedEventChannel create_typed_channel (in CosNotification::QoSProperties initial_QoS, + in CosNotification::AdminProperties initial_admin, + out CosNotifyChannelAdmin::ChannelID id) + raises( CosNotification::UnsupportedQoS, CosNotification::UnsupportedAdmin); + + CosNotifyChannelAdmin::ChannelIDSeq get_all_typed_channels(); + + TypedEventChannel get_typed_event_channel (in CosNotifyChannelAdmin::ChannelID id) + raises (CosNotifyChannelAdmin::ChannelNotFound); + }; // TypedEventChannelFactory +}; // CosTypedNotifyChannelAdmin + +#endif /* ifndef _COS_TYPED_NOTIFICATION_IDL_ */ diff --git a/lib/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile new file mode 100644 index 0000000000..637c633e52 --- /dev/null +++ b/lib/cosNotification/src/Makefile @@ -0,0 +1,369 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk + +ifeq ($(TYPE),debug) +ERL_COMPILE_FLAGS += -Ddebug -W +endif + +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(COSNOTIFICATION_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/cosNotification-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES = \ + CosNotification_Common \ + CosNotifyChannelAdmin_ConsumerAdmin_impl \ + CosNotifyChannelAdmin_EventChannelFactory_impl \ + CosNotifyChannelAdmin_EventChannel_impl \ + CosNotifyChannelAdmin_SupplierAdmin_impl \ + CosNotifyFilter_Filter_impl \ + CosNotifyFilter_MappingFilter_impl \ + CosNotifyFilter_FilterFactory_impl \ + PullerConsumer_impl \ + PullerSupplier_impl \ + PusherConsumer_impl \ + PusherSupplier_impl \ + cosNotificationApp \ + cosNotification_Scanner \ + cosNotification_Filter \ + cosNotification_eventDB + +ERL_FILES = $(MODULES:%=%.erl) +HRL_FILES = \ + CosNotification_Definitions.hrl + +YECC_FILES = cosNotification_Grammar.yrl + +GEN_YECC_ERL_FILES = cosNotification_Grammar.erl + +GEN_YECC_HRL_FILES = + +GEN_NOTIFICATION_ERL_FILES = \ + oe_CosNotification.erl \ + CosNotification.erl \ + CosNotification_AdminPropertiesAdmin.erl \ + CosNotification_EventHeader.erl \ + CosNotification_EventType.erl \ + CosNotification_FixedEventHeader.erl \ + CosNotification_NamedPropertyRange.erl \ + CosNotification_Property.erl \ + CosNotification_PropertyError.erl \ + CosNotification_PropertyRange.erl \ + CosNotification_QoSAdmin.erl \ + CosNotification_StructuredEvent.erl \ + CosNotification_UnsupportedAdmin.erl \ + CosNotification_UnsupportedQoS.erl \ + CosNotification_EventBatch.erl \ + CosNotification_EventTypeSeq.erl \ + CosNotification_NamedPropertyRangeSeq.erl \ + CosNotification_PropertyErrorSeq.erl \ + CosNotification_PropertySeq.erl + +GEN_CHANNELADMIN_ERL_FILES = \ + oe_CosNotifyChannelAdmin.erl \ + CosNotifyChannelAdmin_AdminLimit.erl \ + CosNotifyChannelAdmin_AdminLimitExceeded.erl \ + CosNotifyChannelAdmin_AdminNotFound.erl \ + CosNotifyChannelAdmin_ChannelNotFound.erl \ + CosNotifyChannelAdmin_ConnectionAlreadyActive.erl \ + CosNotifyChannelAdmin_ConnectionAlreadyInactive.erl \ + CosNotifyChannelAdmin_ConsumerAdmin.erl \ + CosNotifyChannelAdmin_EventChannel.erl \ + CosNotifyChannelAdmin_EventChannelFactory.erl \ + CosNotifyChannelAdmin_NotConnected.erl \ + CosNotifyChannelAdmin_ProxyConsumer.erl \ + CosNotifyChannelAdmin_ProxyNotFound.erl \ + CosNotifyChannelAdmin_ProxyPullConsumer.erl \ + CosNotifyChannelAdmin_ProxyPullSupplier.erl \ + CosNotifyChannelAdmin_ProxyPushConsumer.erl \ + CosNotifyChannelAdmin_ProxyPushSupplier.erl \ + CosNotifyChannelAdmin_ProxySupplier.erl \ + CosNotifyChannelAdmin_SequenceProxyPullConsumer.erl \ + CosNotifyChannelAdmin_SequenceProxyPullSupplier.erl \ + CosNotifyChannelAdmin_SequenceProxyPushConsumer.erl \ + CosNotifyChannelAdmin_SequenceProxyPushSupplier.erl \ + CosNotifyChannelAdmin_StructuredProxyPullConsumer.erl \ + CosNotifyChannelAdmin_StructuredProxyPullSupplier.erl \ + CosNotifyChannelAdmin_StructuredProxyPushConsumer.erl \ + CosNotifyChannelAdmin_StructuredProxyPushSupplier.erl \ + CosNotifyChannelAdmin_SupplierAdmin.erl \ + CosNotifyChannelAdmin_AdminIDSeq.erl \ + CosNotifyChannelAdmin_ChannelIDSeq.erl \ + CosNotifyChannelAdmin_ProxyIDSeq.erl + +GEN_NOTIFYFILTER_ERL_FILES = \ + oe_CosNotifyFilter.erl \ + CosNotifyFilter_CallbackNotFound.erl \ + CosNotifyFilter_ConstraintExp.erl \ + CosNotifyFilter_ConstraintInfo.erl \ + CosNotifyFilter_ConstraintNotFound.erl \ + CosNotifyFilter_DuplicateConstraintID.erl \ + CosNotifyFilter_Filter.erl \ + CosNotifyFilter_FilterAdmin.erl \ + CosNotifyFilter_FilterFactory.erl \ + CosNotifyFilter_FilterNotFound.erl \ + CosNotifyFilter_InvalidConstraint.erl \ + CosNotifyFilter_InvalidGrammar.erl \ + CosNotifyFilter_InvalidValue.erl \ + CosNotifyFilter_MappingConstraintInfo.erl \ + CosNotifyFilter_MappingConstraintPair.erl \ + CosNotifyFilter_MappingFilter.erl \ + CosNotifyFilter_UnsupportedFilterableData.erl \ + CosNotifyFilter_CallbackIDSeq.erl \ + CosNotifyFilter_ConstraintExpSeq.erl \ + CosNotifyFilter_ConstraintIDSeq.erl \ + CosNotifyFilter_ConstraintInfoSeq.erl \ + CosNotifyFilter_FilterIDSeq.erl \ + CosNotifyFilter_MappingConstraintInfoSeq.erl \ + CosNotifyFilter_MappingConstraintPairSeq.erl + +GEN_NOTIFYCOMM_ERL_FILES = \ + oe_CosNotifyComm.erl \ + CosNotifyComm_InvalidEventType.erl \ + CosNotifyComm_NotifyPublish.erl \ + CosNotifyComm_NotifySubscribe.erl \ + CosNotifyComm_PullConsumer.erl \ + CosNotifyComm_PullSupplier.erl \ + CosNotifyComm_PushConsumer.erl \ + CosNotifyComm_PushSupplier.erl \ + CosNotifyComm_SequencePullConsumer.erl \ + CosNotifyComm_SequencePullSupplier.erl \ + CosNotifyComm_SequencePushConsumer.erl \ + CosNotifyComm_SequencePushSupplier.erl \ + CosNotifyComm_StructuredPullConsumer.erl \ + CosNotifyComm_StructuredPullSupplier.erl \ + CosNotifyComm_StructuredPushConsumer.erl \ + CosNotifyComm_StructuredPushSupplier.erl \ + +GEN_OE_EVENTCOMM_ERL_FILES = \ + oe_cosNotificationAppComm.erl \ + oe_CosNotificationComm_Event.erl + +EXTERNAL_INC_PATH = ../include + +GEN_NOTIFICATION_HRL_FILES = \ + oe_CosNotification.hrl \ + CosNotification.hrl \ + CosNotification_AdminPropertiesAdmin.hrl \ + CosNotification_QoSAdmin.hrl \ + +EXTERNAL_GEN_NOTIFICATION_HRL_FILES = \ + $(GEN_NOTIFICATION_HRL_FILES:%=$(EXTERNAL_INC_PATH)/%) + +GEN_CHANNELADMIN_HRL_FILES = \ + oe_CosNotifyChannelAdmin.hrl \ + CosNotifyChannelAdmin.hrl \ + CosNotifyChannelAdmin_ConsumerAdmin.hrl \ + CosNotifyChannelAdmin_EventChannel.hrl \ + CosNotifyChannelAdmin_EventChannelFactory.hrl \ + CosNotifyChannelAdmin_ProxyConsumer.hrl \ + CosNotifyChannelAdmin_ProxyPullConsumer.hrl \ + CosNotifyChannelAdmin_ProxyPullSupplier.hrl \ + CosNotifyChannelAdmin_ProxyPushConsumer.hrl \ + CosNotifyChannelAdmin_ProxyPushSupplier.hrl \ + CosNotifyChannelAdmin_ProxySupplier.hrl \ + CosNotifyChannelAdmin_SequenceProxyPullConsumer.hrl \ + CosNotifyChannelAdmin_SequenceProxyPullSupplier.hrl \ + CosNotifyChannelAdmin_SequenceProxyPushConsumer.hrl \ + CosNotifyChannelAdmin_SequenceProxyPushSupplier.hrl \ + CosNotifyChannelAdmin_StructuredProxyPullConsumer.hrl \ + CosNotifyChannelAdmin_StructuredProxyPullSupplier.hrl \ + CosNotifyChannelAdmin_StructuredProxyPushConsumer.hrl \ + CosNotifyChannelAdmin_StructuredProxyPushSupplier.hrl \ + CosNotifyChannelAdmin_SupplierAdmin.hrl \ + +EXTERNAL_GEN_CHANNELADMIN_HRL_FILES = \ + $(GEN_CHANNELADMIN_HRL_FILES:%=$(EXTERNAL_INC_PATH)/%) + +GEN_NOTIFYFILTER_HRL_FILES = \ + oe_CosNotifyFilter.hrl \ + CosNotifyFilter.hrl \ + CosNotifyFilter_Filter.hrl \ + CosNotifyFilter_FilterAdmin.hrl \ + CosNotifyFilter_FilterFactory.hrl \ + CosNotifyFilter_MappingFilter.hrl + +EXTERNAL_GEN_NOTIFYFILTER_HRL_FILES = \ + $(GEN_NOTIFYFILTER_HRL_FILES:%=$(EXTERNAL_INC_PATH)/%) + +GEN_NOTIFYCOMM_HRL_FILES = \ + oe_CosNotifyComm.hrl \ + CosNotifyComm.hrl \ + CosNotifyComm_NotifyPublish.hrl \ + CosNotifyComm_NotifySubscribe.hrl \ + CosNotifyComm_PullConsumer.hrl \ + CosNotifyComm_PullSupplier.hrl \ + CosNotifyComm_PushConsumer.hrl \ + CosNotifyComm_PushSupplier.hrl \ + CosNotifyComm_SequencePullConsumer.hrl \ + CosNotifyComm_SequencePullSupplier.hrl \ + CosNotifyComm_SequencePushConsumer.hrl \ + CosNotifyComm_SequencePushSupplier.hrl \ + CosNotifyComm_StructuredPullConsumer.hrl \ + CosNotifyComm_StructuredPullSupplier.hrl \ + CosNotifyComm_StructuredPushConsumer.hrl \ + CosNotifyComm_StructuredPushSupplier.hrl \ + +EXTERNAL_GEN_NOTIFYCOMM_HRL_FILES = \ + $(GEN_NOTIFYCOMM_HRL_FILES:%=$(EXTERNAL_INC_PATH)/%) + +GEN_OE_EVENTCOMM_HRL_FILES = \ + oe_cosNotificationAppComm.hrl \ + oe_CosNotificationComm.hrl \ + oe_CosNotificationComm_Event.hrl + +GEN_ERL_FILES = \ + $(GEN_NOTIFICATION_ERL_FILES) \ + $(GEN_OE_EVENTCOMM_ERL_FILES) \ + $(GEN_NOTIFYCOMM_ERL_FILES) \ + $(GEN_NOTIFYFILTER_ERL_FILES) \ + $(GEN_CHANNELADMIN_ERL_FILES) \ + $(GEN_YECC_ERL_FILES) + +GEN_HRL_FILES = \ + $(EXTERNAL_GEN_NOTIFICATION_HRL_FILES) \ + $(GEN_OE_EVENTCOMM_HRL_FILES) \ + $(EXTERNAL_GEN_NOTIFYCOMM_HRL_FILES) \ + $(EXTERNAL_GEN_NOTIFYFILTER_HRL_FILES) \ + $(EXTERNAL_GEN_CHANNELADMIN_HRL_FILES) \ + $(GEN_YECC_HRL_FILES) + + +GEN_FILES = \ + $(GEN_HRL_FILES) \ + $(GEN_ERL_FILES) + +TARGET_FILES = \ + $(GEN_ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR)) \ + $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +IDL_FILES = \ + CosNotification.idl \ + CosNotifyChannelAdmin.idl \ + CosNotifyFilter.idl \ + CosNotifyComm.idl \ + cosNotificationAppComm.idl + +APPUP_FILE = cosNotification.appup +APPUP_SRC = $(APPUP_FILE).src +APPUP_TARGET = $(EBIN)/$(APPUP_FILE) + +APP_FILE = cosNotification.app +APP_SRC = $(APP_FILE).src +APP_TARGET = $(EBIN)/$(APP_FILE) + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_IDL_FLAGS += -pa $(ERL_TOP)/lib/cosNotification/ebin \ + -pa $(ERL_TOP)/lib/ic/ebin\ + -pa $(ERL_TOP)/lib/orber/ebin \ + -pa $(ERL_TOP)/lib/cosEvent/ebin \ + -pa $(ERL_TOP)/lib/cosTime/ebin \ + -I$(ERL_TOP)/lib/cosEvent/src + +# The -pa option is just used temporary until erlc can handle +# includes from other directories than ../include . +ERL_COMPILE_FLAGS += \ + $(ERL_IDL_FLAGS) \ + -pa $(ERL_TOP)/lib/orber/include \ + -pa $(ERL_TOP)/lib/cosNotification/include \ + -pa $(ERL_TOP)/lib/cosEvent/include \ + -pa $(ERL_TOP)/lib/cosTime/include \ + -I$(ERL_TOP)/lib/orber/include \ + -I$(ERL_TOP)/lib/cosNotification/include \ + -I$(ERL_TOP)/lib/cosEvent/include \ + -I$(ERL_TOP)/lib/cosTime/include \ + +'{parse_transform,sys_pre_attributes}' \ + +'{attribute,insert,app_vsn,"cosNotification_$(COSNOTIFICATION_VSN)"}' + +YECC_COMPILE_FLAGS = + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) + +debug: + @${MAKE} TYPE=debug + +cleanb: + rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) + rm -f errs core *~ + +clean: + rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) + rm -f errs core *~ + +$(APP_TARGET): $(APP_SRC) + sed -e 's;%VSN%;$(VSN);' $< > $@ + +$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ + +docs: + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- +$(GEN_NOTIFICATION_ERL_FILES) $(EXTERNAL_GEN_NOTIFICATION_HRL_FILES): CosNotification.idl + erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotification.cfg"}' CosNotification.idl + mv $(GEN_NOTIFICATION_HRL_FILES) $(EXTERNAL_INC_PATH) +$(GEN_CHANNELADMIN_ERL_FILES) $(EXTERNAL_GEN_CHANNELADMIN_HRL_FILES): CosNotifyChannelAdmin.idl + erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyChannelAdmin.cfg"}' CosNotifyChannelAdmin.idl + mv $(GEN_CHANNELADMIN_HRL_FILES) $(EXTERNAL_INC_PATH) +$(GEN_NOTIFYFILTER_ERL_FILES) $(EXTERNAL_GEN_NOTIFYFILTER_HRL_FILES): CosNotifyFilter.idl + erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyFilter.cfg"}' CosNotifyFilter.idl + mv $(GEN_NOTIFYFILTER_HRL_FILES) $(EXTERNAL_INC_PATH) +$(GEN_OE_EVENTCOMM_ERL_FILES) $(GEN_OE_EVENTCOMM_HRL_FILES): cosNotificationAppComm.idl + erlc $(ERL_IDL_FLAGS) +'{cfgfile,"cosNotificationComm.cfg"}' cosNotificationAppComm.idl +$(GEN_NOTIFYCOMM_ERL_FILES) $(EXTERNAL_GEN_NOTIFYCOMM_HRL_FILES): CosNotifyComm.idl + erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosNotifyComm.cfg"}' CosNotifyComm.idl + mv $(GEN_NOTIFYCOMM_HRL_FILES) $(EXTERNAL_INC_PATH) +$(GEN_YECC_ERL_FILES) $(GEN_YECC_HRL_FILES): cosNotification_Grammar.yrl + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(YECC_FILES) $(RELSYSDIR)/src + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILES) $(YECC_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/include + $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + +release_docs_spec: diff --git a/lib/cosNotification/src/PullerConsumer_impl.erl b/lib/cosNotification/src/PullerConsumer_impl.erl new file mode 100644 index 0000000000..fe6f9f8968 --- /dev/null +++ b/lib/cosNotification/src/PullerConsumer_impl.erl @@ -0,0 +1,773 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : PullerConsumer_impl.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module('PullerConsumer_impl'). + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% cosEvent files. +-include_lib("cosEvent/include/CosEventChannelAdmin.hrl"). +-include_lib("cosEvent/include/CosEventComm.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::ProxyPullConsumer ------------- +-export([connect_any_pull_supplier/4]). + +%%----- CosNotifyChannelAdmin::SequenceProxyPullConsumer ----- +-export([connect_sequence_pull_supplier/4]). + +%%----- CosNotifyChannelAdmin::StructuredProxyPullConsumer --- +-export([connect_structured_pull_supplier/4]). + +%%----- CosNotifyChannelAdmin::*ProxyPullConsumer ------------ +-export([suspend_connection/3, + resume_connection/3]). + +%%----- Inherit from CosNotifyChannelAdmin::ProxyConsumer ---- +-export([obtain_subscription_types/4, + validate_event_qos/4]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotifyComm::NotifyPublish ------------ +-export([offer_change/5]). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ------------ +-export([add_filter/4, + remove_filter/4, + get_filter/4, + get_all_filters/3, + remove_all_filters/3]). + +%%----- Inherit from CosEventComm::PullConsumer ------------- +-export([disconnect_pull_consumer/3]). + +%%----- Inherit from CosNotifyComm::SequencePullConsumer ---- +-export([disconnect_sequence_pull_consumer/3]). + +%%----- Inherit from CosNotifyComm::StructuredPullConsumer -- +-export([disconnect_structured_pull_consumer/3]). + +%%----- Inherit from CosEventChannelAdmin::ProxyPullConsumer +-export([connect_pull_supplier/4]). + + +%% Attributes (external) CosNotifyChannelAdmin::ProxySupplier +-export(['_get_MyType'/3, + '_get_MyAdmin'/3]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {myType, + myAdmin, + myAdminPid, + myChannel, + myFilters = [], + myOperator, + idCounter = 0, + client, + qosGlobal, + qosLocal, + suspended = false, + pullTimer, + pullInterval, + publishType = false, + etsR, + eventCounter = 0, + eventDB, + this}). + +%% Data structures constructors +-define(get_InitState(_MyT, _MyA, _MyAP, _QS, _LQS, _Ch, _PI, _MyOP, _GT, _GL, _TR), + #state{myType = _MyT, + myAdmin = _MyA, + myAdminPid = _MyAP, + myChannel = _Ch, + myOperator = _MyOP, + qosGlobal = _QS, + qosLocal = _LQS, + pullInterval = _PI, + etsR = ets:new(oe_ets, [set, protected]), + eventDB = cosNotification_eventDB:create_db(_LQS, _GT, _GL, _TR)}). + +%%-------------- Data structures selectors ----------------- +%% Attributes +-define(get_MyType(S), S#state.myType). +-define(get_MyAdmin(S), S#state.myAdmin). +-define(get_MyAdminPid(S), S#state.myAdminPid). +-define(get_MyChannel(S), S#state.myChannel). +-define(get_MyOperator(S), S#state.myOperator). +%% Client Object +-define(get_Client(S), S#state.client). +%% QoS +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +%% Filters +-define(get_Filter(S, I), find_obj(lists:keysearch(I, 1, S#state.myFilters))). +-define(get_AllFilter(S), S#state.myFilters). +-define(get_AllFilterID(S), find_ids(S#state.myFilters)). +%% Admin +-define(get_PullInterval(S), S#state.pullInterval). +-define(get_PullTimer(S), S#state.pullTimer). +-define(get_PacingInterval(S), round(?not_GetPacingInterval((S#state.qosLocal))/10000000)). +-define(get_BatchLimit(S), ?not_GetMaximumBatchSize((S#state.qosLocal))). +%% Publish +-define(get_AllPublish(S), lists:flatten(ets:match(S#state.etsR, + {'$1',publish}))). +-define(get_PublishType(S), S#state.publishType). +%% ID +-define(get_IdCounter(S), S#state.idCounter). +%% Event +-define(get_Event(S), cosNotification_eventDB:get_event(S#state.eventDB)). +-define(get_Events(S,M), cosNotification_eventDB:get_events(S#state.eventDB, M)). +-define(get_EventCounter(S), S#state.eventCounter). + +%%-------------- Data structures modifiers ----------------- +%% Client Object +-define(set_Client(S,D), S#state{client=D}). +-define(del_Client(S), S#state{client=undefined}). +-define(set_Suspended(S), S#state{client=true}). +-define(set_NotSuspended(S), S#state{client=false}). +-define(set_Unconnected(S), S#state{client=undefined}). +%% QoS +-define(set_LocalQoS(S,D), S#state{qosLocal=D}). +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +%% Filters +-define(add_Filter(S,I,O), S#state{myFilters=[{I,O}|S#state.myFilters]}). +-define(del_Filter(S,I), S#state{myFilters= + delete_obj(lists:keydelete(I, 1, S#state.myFilters), + S#state.myFilters)}). +-define(del_AllFilter(S), S#state{myFilters=[]}). +%% Admin +-define(set_PullInterval(S,V), S#state{pullInterval=V}). +-define(set_PullTimer(S,T), S#state{pullTimer=T}). +%% Publish +-define(add_Publish(S,E), ets:insert(S#state.etsR, {E, publish})). +-define(del_Publish(S,E), ets:delete(S#state.etsR, E)). +-define(set_PublishType(S,T), S#state{publishType=T}). +%% ID +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). +%% Event +-define(add_Event(S,E), cosNotification_eventDB:add_event(S#state.eventDB, E)). +-define(update_EventDB(S,Q), S#state{eventDB= + cosNotification_eventDB:update(S#state.eventDB, Q)}). + +-define(set_EventCounter(S,V), S#state{eventCounter=V}). +-define(add_to_EventCounter(S,V),S#state{eventCounter=S#state.eventCounter+V}). +-define(reset_EventCounter(S), S#state{eventCounter=0}). +-define(increase_EventCounter(S),S#state{eventCounter=(S#state.eventCounter+1)}). +-define(decrease_EventCounter(S),S#state{eventCounter=S#state.eventCounter-1}). +-define(add_ToEventCounter(S,A), S#state{eventCounter=(S#state.eventCounter+A)}). +-define(sub_FromEventCounter(S,_A), S#state{eventCounter=(S#state.eventCounter-_A)}). +-define(set_EventCounterTo(S,V), S#state{eventCounter=V}). + +%%-------------- MISC ---------------------------------------- +-define(is_ANY(S), S#state.myType == 'PULL_ANY'). +-define(is_STRUCTURED(S), S#state.myType == 'PULL_STRUCTURED'). +-define(is_SEQUENCE(S), S#state.myType == 'PULL_SEQUENCE'). +-define(is_ANDOP(S), S#state.myOperator == 'AND_OP'). +-define(is_UnConnected(S), S#state.client == undefined). +-define(is_Connected(S), S#state.client =/= undefined). +-define(is_Suspended(S), S#state.suspended == true). +-define(is_NotSuspended(S), S#state.suspended == false). +-define(is_PersistentConnection(S), + ?not_GetConnectionReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_PersistentEvent(S), + ?not_GetEventReliability((S#state.qosLocal)) == ?not_Persistent). + +%%-----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%------------------------------------------------------------ + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?DBG("INFO: ~p~n", [Info]), + case Info of + {'EXIT', Pid, Reason} when ?get_MyAdminPid(State) == Pid-> + ?DBG("PARENT ADMIN: ~p TERMINATED.~n",[Reason]), + {stop, Reason, State}; + {'EXIT', _Pid, _Reason} -> + ?DBG("PROXYPUSHSUPPLIER: ~p TERMINATED.~n",[_Reason]), + {noreply, State}; + pull -> + try_pull_events(State); + _ -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([MyType, MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, Options, Operator]) -> + process_flag(trap_exit, true), + Secs = timer:seconds('CosNotification_Common':get_option(pullInterval, + Options, + ?not_DEFAULT_SETTINGS)), + GCTime = 'CosNotification_Common':get_option(gcTime, Options, + ?not_DEFAULT_SETTINGS), + GCLimit = 'CosNotification_Common':get_option(gcLimit, Options, + ?not_DEFAULT_SETTINGS), + TimeRef = 'CosNotification_Common':get_option(timeService, Options, + ?not_DEFAULT_SETTINGS), + timer:start(), + {ok, ?get_InitState(MyType, MyAdmin, MyAdminPid, InitQoS, + LQS, MyChannel, Secs, Operator, GCTime, + GCLimit, TimeRef)}. + +terminate(_Reason, State) when ?is_UnConnected(State) -> + %% We are currently not connected to a client. Hence, no need for sending + %% a disconnect request. + stop_timer(State), + ok; +terminate(_Reason, State) when ?is_ANY(State) -> + stop_timer(State), + 'CosNotification_Common':disconnect('CosEventComm_PullSupplier', + disconnect_pull_supplier, + ?get_Client(State)); +terminate(_Reason, State) when ?is_SEQUENCE(State) -> + stop_timer(State), + 'CosNotification_Common':disconnect('CosNotifyComm_SequencePullSupplier', + disconnect_sequence_pull_supplier, + ?get_Client(State)); +terminate(_Reason, State) when ?is_STRUCTURED(State) -> + stop_timer(State), + 'CosNotification_Common':disconnect('CosNotifyComm_StructuredPullSupplier', + disconnect_structured_pull_supplier, + ?get_Client(State)). + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_ProxyConsumer attributes ------ +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyType' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyType'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyType(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyAdmin' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyAdmin'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyAdmin(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----- CosEventChannelAdmin::ProxyPullConsumer ------------- +%%----------------------------------------------------------% +%% function : connect_pull_supplier +%% Arguments: Client - CosEventComm::PullSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%% Both exceptions from CosEventChannelAdmin!!! +%%----------------------------------------------------------- +connect_pull_supplier(OE_THIS, OE_FROM, State, Client) -> + connect_any_pull_supplier(OE_THIS, OE_FROM, State, Client). + +%%----- CosNotifyChannelAdmin::ProxyPullConsumer ------------ +%%----------------------------------------------------------% +%% function : connect_any_pull_supplier +%% Arguments: Client - CosEventComm::PullSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%% Both exceptions from CosEventChannelAdmin!!! +%%----------------------------------------------------------- +connect_any_pull_supplier(OE_THIS, _OE_FROM, State, Client) when ?is_ANY(State) -> + 'CosNotification_Common':type_check(Client, 'CosEventComm_PullSupplier'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + NewState = start_timer(State), + {reply, ok, NewState#state{client = Client, this = OE_THIS}} + end; +connect_any_pull_supplier(_, _, _,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::SequenceProxyPullConsumer ---- +%%----------------------------------------------------------% +%% function : connect_sequence_pull_supplier +%% Arguments: Client - CosNotifyComm::SequencePullSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%%----------------------------------------------------------- +connect_sequence_pull_supplier(OE_THIS, _OE_FROM, State, Client) when ?is_SEQUENCE(State) -> + 'CosNotification_Common':type_check(Client, 'CosNotifyComm_SequencePullSupplier'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + NewState = start_timer(State), + {reply, ok, NewState#state{client = Client, this = OE_THIS}} + end; +connect_sequence_pull_supplier(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::StructuredProxyPullConsumer -- +%%----------------------------------------------------------% +%% function : connect_structured_pull_supplier +%% Arguments: Client - CosNotifyComm::StructuredPullSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%%----------------------------------------------------------- +connect_structured_pull_supplier(OE_THIS, _OE_FROM, State, Client) when ?is_STRUCTURED(State) -> + 'CosNotification_Common':type_check(Client, 'CosNotifyComm_StructuredPullSupplier'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + NewState = start_timer(State), + {reply, ok, NewState#state{client = Client, this = OE_THIS}} + end; +connect_structured_pull_supplier(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::*ProxyPullConsumer ----------- +%%----------------------------------------------------------% +%% function : suspend_connection +%% Arguments: +%% Returns : ok | {'EXCEPTION', #'ConnectionAlreadyInactive'{}} | +%% {'EXCEPTION', #'NotConneced'{}} +%%----------------------------------------------------------- +suspend_connection(_OE_THIS, _OE_FROM, State) when ?is_Connected(State) -> + if + ?is_Suspended(State) -> + corba:raise(#'CosNotifyChannelAdmin_ConnectionAlreadyInactive'{}); + true -> + stop_timer(State), + {reply, ok, ?set_Suspended(State)} + end; +suspend_connection(_, _, _) -> + corba:raise(#'CosNotifyChannelAdmin_NotConnected'{}). + +%%----------------------------------------------------------% +%% function : resume_connection +%% Arguments: +%% Returns : ok | {'EXCEPTION', #'ConnectionAlreadyActive'{}} | +%% {'EXCEPTION', #'NotConneced'{}} +%%----------------------------------------------------------- +resume_connection(_OE_THIS, _OE_FROM, State) when ?is_Connected(State) -> + if + ?is_NotSuspended(State) -> + corba:raise(#'CosNotifyChannelAdmin_ConnectionAlreadyActive'{}); + true -> + NewState = start_timer(State), + {reply, ok, ?set_NotSuspended(NewState)} + end; +resume_connection(_, _, _) -> + corba:raise(#'CosNotifyChannelAdmin_NotConnected'{}). + +%%----- Inherit from CosNotifyChannelAdmin::ProxyConsumer --- +%%----------------------------------------------------------% +%% function : obtain_subscription_types +%% Arguments: Mode - enum 'ObtainInfoMode' (CosNotifyChannelAdmin) +%% Returns : CosNotification::EventTypeSeq +%%----------------------------------------------------------- +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_OFF') -> + {reply, ?get_AllPublish(State), ?set_PublishType(State, false)}; +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_ON') -> + {reply, ?get_AllPublish(State), ?set_PublishType(State, true)}; +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_OFF') -> + {reply, [], ?set_PublishType(State, false)}; +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_ON') -> + {reply, [], ?set_PublishType(State, true)}; +obtain_subscription_types(_,_,_,What) -> + orber:dbg("[~p] PullerConsumer:obtain_subscription_types(~p);~n" + "Incorrect enumerant", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : validate_event_qos +%% Arguments: RequiredQoS - CosNotification::QoSProperties +%% Returns : ok | {'EXCEPTION', #'UnsupportedQoS'{}} +%% AvilableQoS - CosNotification::NamedPropertyRangeSeq (out) +%%----------------------------------------------------------- +validate_event_qos(_OE_THIS, _OE_FROM, State, RequiredQoS) -> + AvilableQoS = 'CosNotification_Common':validate_event_qos(RequiredQoS, + ?get_LocalQoS(State)), + {reply, {ok, AvilableQoS}, State}. + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {NewQoS, LQS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + NewState = ?update_EventDB(State, LQS), + {reply, ok, ?set_BothQoS(NewState, NewQoS, LQS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} +%% {ok, CosNotification::NamedPropertyRangeSeq} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotifyComm::NotifyPublish ----------- +%%----------------------------------------------------------% +%% function : offer_change +%% Arguments: Added - #'CosNotification_EventType'{} +%% Removed - #'CosNotification_EventType'{} +%% Returns : ok | +%% {'EXCEPTION', #'CosNotifyComm_InvalidEventType'{}} +%%----------------------------------------------------------- +offer_change(_OE_THIS, _OE_FROM, State, Added, Removed) -> + cosNotification_Filter:validate_types(Added), + cosNotification_Filter:validate_types(Removed), + %% On this "side" we don't really care about which + %% type of events the client will supply. + %% Perhaps, later on, if we want to check this against Filters + %% associated with this object we may change this approach, i.e., if + %% the filter will not allow passing certain event types. But the + %% user should see to that that situation never occurs. It would add + %% extra overhead. Also see PusherSupplier- and PullerSuppler- + %% 'subscription_change'. + update_publish(add, State, Added), + update_publish(remove, State, Removed), + case ?get_PublishType(State) of + true -> + %% Perhaps we should handle exception here?! + %% Probably not. Better to stay "on-line". + catch 'CosNotifyComm_NotifySubscribe': + subscription_change(?get_Client(State), Added, Removed), + ok; + _-> + ok + end, + {reply, ok, State}. + +update_publish(_, _, [])-> + ok; +update_publish(add, State, [H|T]) -> + ?add_Publish(State, H), + update_publish(add, State, T); +update_publish(remove, State, [H|T]) -> + ?del_Publish(State, H), + update_publish(remove, State, T). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ----------- +%%----------------------------------------------------------% +%% function : add_filter +%% Arguments: Filter - CosNotifyFilter::Filter +%% Returns : FilterID - long +%%----------------------------------------------------------- +add_filter(_OE_THIS, _OE_FROM, State, Filter) -> + 'CosNotification_Common':type_check(Filter, 'CosNotifyFilter_Filter'), + FilterID = ?new_Id(State), + NewState = ?set_IdCounter(State, FilterID), + {reply, FilterID, ?add_Filter(NewState, FilterID, Filter)}. + +%%----------------------------------------------------------% +%% function : remove_filter +%% Arguments: FilterID - long +%% Returns : ok +%%----------------------------------------------------------- +remove_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ok, ?del_Filter(State, FilterID)}; +remove_filter(_,_,_,What) -> + orber:dbg("[~p] PullerConsumer:remove_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_filter +%% Arguments: FilterID - long +%% Returns : Filter - CosNotifyFilter::Filter | +%% {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}} +%%----------------------------------------------------------- +get_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ?get_Filter(State, FilterID), State}; +get_filter(_,_,_,What) -> + orber:dbg("[~p] PullerConsumer:get_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_filters +%% Arguments: - +%% Returns : Filter - CosNotifyFilter::FilterIDSeq +%%----------------------------------------------------------- +get_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_AllFilterID(State), State}. + +%%----------------------------------------------------------% +%% function : remove_all_filters +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ok, ?del_AllFilter(State)}. + +%%----- Inherit from CosEventComm::PullConsumer ------------- +%%----------------------------------------------------------% +%% function : disconnect_pull_consumer +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_pull_consumer(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----- Inherit from CosNotifyComm::SequencePullConsumer ---- +%%----------------------------------------------------------% +%% function : disconnect_sequence_pull_consumer +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_sequence_pull_consumer(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----- Inherit from CosNotifyComm::StructuredPullConsumer ---- +%%----------------------------------------------------------% +%% function : disconnect_structured_pull_consumer +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_structured_pull_consumer(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj({value, {_, Obj}}) -> Obj; +find_obj(_) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}. + +find_ids(List) -> find_ids(List, []). +find_ids([], Acc) -> Acc; +find_ids([{I,_}|T], Acc) -> find_ids(T, [I|Acc]); +find_ids(What, _) -> + orber:dbg("[~p] PullerConsumer:find_ids();~n" + "Id corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +%% Delete a single object. +%% The list don not differ, i.e., no filter removed, raise exception. +delete_obj(List,List) -> corba:raise(#'CosNotifyFilter_FilterNotFound'{}); +delete_obj(List,_) -> List. + +%% Start timer which send a message each time we should pull for new events. +start_timer(State) -> + case catch timer:send_interval(?get_PullInterval(State), pull) of + {ok,PullTRef} -> + ?DBG("PULL CONSUMER STARTED PULL TIMER ~p~n", + [?get_PullInterval(State)]), + ?set_PullTimer(State, PullTRef); + What -> + orber:dbg("[~p] PullerConsumer:start_timer();~n" + "Unable to invoke timer:send_interval/2: ~p", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. +stop_timer(State) -> + ?DBG("PULL CONSUMER STOPPED TIMER~n",[]), + timer:cancel(?get_PullTimer(State)). + +%% Try pull event(s); which method is determined by which type this proxy is. +try_pull_events(State) when ?is_ANY(State) -> + case catch 'CosEventComm_PullSupplier':try_pull(?get_Client(State)) of + {_,false} -> + {noreply, State}; + {Event, true} -> + case ?not_isConvertedStructured(Event) of + true -> + forward(seq, State, + cosNotification_eventDB:filter_events([any:get_value(Event)], + ?get_AllFilter(State))); + _ -> + forward(any, State, + cosNotification_eventDB:filter_events([Event], + ?get_AllFilter(State))) + end; + _-> + {noreply, State} + end; +try_pull_events(State) when ?is_SEQUENCE(State) -> + case catch 'CosNotifyComm_SequencePullSupplier': + try_pull_structured_events(?get_Client(State), ?get_BatchLimit(State)) of + {_,false} -> + {noreply, State}; + {EventSeq, true} -> + %% We cannot convert parts of the sequence to any, event though they + %% are converted from any to structured. Would be 'impossible' to send. + forward(seq, State, + cosNotification_eventDB:filter_events(EventSeq, + ?get_AllFilter(State))); + _-> + {noreply, State} + end; +try_pull_events(State) when ?is_STRUCTURED(State) -> + case catch 'CosNotifyComm_StructuredPullSupplier': + try_pull_structured_event(?get_Client(State)) of + {_,false} -> + {noreply, State}; + {Event, true} when ?not_isConvertedAny(Event) -> + forward(any, State, + cosNotification_eventDB:filter_events([Event#'CosNotification_StructuredEvent'.remainder_of_body], + ?get_AllFilter(State))); + {Event, true} -> + forward(seq, State, + cosNotification_eventDB:filter_events([Event], + ?get_AllFilter(State))); + _-> + {noreply, State} + end. + + + +%% Forward events +forward(_, State, {[], _}) when ?is_ANDOP(State) -> + %% Did not pass filtering. Since AND no need to pass on. + {noreply, State}; +forward(Type, State, {[], Failed}) -> + %% Did not pass filtering, but since OR it may pass Admin filters, hence, pass + %% on to Admin + forward(Type, State, Failed, ?get_MyAdmin(State), 'MATCH'); +forward(Type, State, {Passed, _}) when ?is_ANDOP(State) -> + %% Did pass filtering, but since AND we must pass it to Admin to check against + %% its Filters. Just ignore the ones that failed. + forward(Type, State, Passed, ?get_MyAdmin(State), 'MATCH'); +forward(Type, State, {Passed, []}) -> + %% Did pass filtering, and since OR we can pass it to the Channel directly. + forward(Type, State, Passed, ?get_MyChannel(State), 'MATCHED'); +forward(Type, State, {Passed, Failed}) -> + %% Some passed filtering, and since OR we can pass the ones that passed directly + %% to the channel and the other ones via the admin. + forward(Type, State, Passed, ?get_MyChannel(State), 'MATCHED'), + forward(Type, State, Failed, ?get_MyAdmin(State), 'MATCH'). + +forward(any, State, [Event], SendTo, Status) -> + case catch oe_CosNotificationComm_Event:callAny(SendTo, Event, Status) of + ok -> + ?DBG("PROXY FORWARD ANY: ~p~n",[Event]), + {noreply, State}; + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + orber:dbg("[~p] PullerConsumer:forward();~n" + "Admin/Channel no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] PullerConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Dropping: ~p", [?LINE, R, Event], ?DEBUG_LEVEL), + {noreply, State}; + R -> + orber:dbg("[~p] PullerConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, R, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, R}]), + {stop, normal, State} + end; +forward(seq, State, Event, SendTo, Status) -> + case catch oe_CosNotificationComm_Event:callSeq(SendTo, Event, Status) of + ok -> + ?DBG("PROXY FORWARD SEQUENCE: ~p~n",[Event]), + {noreply, State}; + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + orber:dbg("[~p] PullerConsumer:forward();~n" + "Admin/Channel no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] PullerConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Dropping: ~p", [?LINE, R, Event], ?DEBUG_LEVEL), + {noreply, State}; + R -> + orber:dbg("[~p] PullerConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, R, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, R}]), + {stop, normal, State} + end. + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/PullerSupplier_impl.erl b/lib/cosNotification/src/PullerSupplier_impl.erl new file mode 100644 index 0000000000..9f12f9c742 --- /dev/null +++ b/lib/cosNotification/src/PullerSupplier_impl.erl @@ -0,0 +1,914 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : PullerSupplier_impl.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module('PullerSupplier_impl'). + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% cosEvent files. +-include_lib("cosEvent/include/CosEventChannelAdmin.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::ProxyPullSupplier ------------- +-export([connect_any_pull_consumer/4]). + +%%----- CosNotifyChannelAdmin::SequenceProxyPullSupplier ----- +-export([connect_sequence_pull_consumer/4]). + +%%----- CosNotifyChannelAdmin::StructuredProxyPullSupplier --- +-export([connect_structured_pull_consumer/4]). + +%%----- Inherit from CosNotifyChannelAdmin::ProxySupplier ---- +-export([obtain_offered_types/4, + validate_event_qos/4]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotifyComm::NotifySubscribe ---------- +-export([subscription_change/5]). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ------------ +-export([add_filter/4, + remove_filter/4, + get_filter/4, + get_all_filters/3, + remove_all_filters/3]). + +%%----- Inherit from CosEventComm::PullSupplier ------------- +-export([pull/3, + try_pull/3, + disconnect_pull_supplier/3]). + +%%----- Inherit from CosNotifyComm::SequencePullSupplier -- +-export([pull_structured_events/4, + try_pull_structured_events/4, + disconnect_sequence_pull_supplier/3]). + +%%----- Inherit from CosNotifyComm::StructuredPullSupplier -- +-export([pull_structured_event/3, + try_pull_structured_event/3, + disconnect_structured_pull_supplier/3]). + +%%----- Inherit from CosEventChannelAdmin::ProxyPullSupplier +-export([connect_pull_consumer/4]). + +%% Attributes (external) CosNotifyChannelAdmin::ProxySupplier +-export(['_get_MyType'/3, + '_get_MyAdmin'/3, + '_get_priority_filter'/3, + '_set_priority_filter'/4, + '_get_lifetime_filter'/3, + '_set_lifetime_filter'/4]). + +%%--------------- Internal ----------------------------------- +%%----- Inherit from cosNotificationComm -------------------- +-export([callAny/5, + callSeq/5]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {myType, + myAdmin, + myAdminPid, + myChannel, + myFilters = [], + myOperator, + idCounter = 0, + prioFil, + lifetFil, + client, + qosGlobal, + qosLocal, + pacingTimer, + respondTo, + subscribeType = false, + subscribeData = true, + etsR, + eventDB}). + +%% Data structures constructors +-define(get_InitState(_MyT, _MyA, _MyAP, _QS, _LQS, _Ch, _MyOp, _GT, _GL, _TR), + #state{myType = _MyT, + myAdmin = _MyA, + myAdminPid= _MyAP, + myChannel = _Ch, + myOperator= _MyOp, + qosGlobal = _QS, + qosLocal = _LQS, + etsR = ets:new(oe_ets, [set, protected]), + eventDB = cosNotification_eventDB:create_db(_LQS, _GT, _GL, _TR)}). + + +%% Data structures selectors +%% Attributes +-define(get_MyType(S), S#state.myType). +-define(get_MyAdmin(S), S#state.myAdmin). +-define(get_MyAdminPid(S), S#state.myAdmin). +-define(get_MyChannel(S), S#state.myChannel). +-define(get_MyOperator(S), S#state.myOperator). +-define(get_PrioFil(S), S#state.prioFil). +-define(get_LifeTFil(S), S#state.lifetFil). +%% Client Object +-define(get_Client(S), S#state.client). +%% QoS +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +%% Filters +-define(get_Filter(S, I), find_obj(lists:keysearch(I, 1, S#state.myFilters))). +-define(get_AllFilter(S), S#state.myFilters). +-define(get_AllFilterID(S), find_ids(S#state.myFilters)). +%% Event +-define(get_Event(S), cosNotification_eventDB:get_event(S#state.eventDB)). +-define(get_Events(S,M), cosNotification_eventDB:get_events(S#state.eventDB, M)). +-define(get_RespondTo(S), S#state.respondTo). +%% Amin +-define(get_PacingTimer(S), S#state.pacingTimer). +-define(get_PacingInterval(S), round(?not_GetPacingInterval((S#state.qosLocal))/10000000)). +-define(get_BatchLimit(S), ?not_GetMaximumBatchSize((S#state.qosLocal))). +%% Subscribe +-define(get_AllSubscribe(S), lists:flatten(ets:match(S#state.etsR, + {'$1',subscribe}))). +-define(get_SubscribeType(S), S#state.subscribeType). +-define(get_SubscribeData(S), S#state.subscribeData). +%% ID +-define(get_IdCounter(S), S#state.idCounter). +-define(get_SubscribeDB(S), S#state.etsR). + +%% Data structures modifiers +%% Attributes +-define(set_PrioFil(S,D), S#state{prioFil=D}). +-define(set_LifeTFil(S,D), S#state{lifetFil=D}). +%% Client Object +-define(set_Client(S,D), S#state{client=D}). +-define(del_Client(S), S#state{client=undefined}). +%% QoS +-define(set_LocalQoS(S,D), S#state{qosLocal=D}). +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +-define(update_EventDB(S,Q), S#state{eventDB= + cosNotification_eventDB:update(S#state.eventDB, Q)}). +%% Filters +-define(add_Filter(S,I,O), S#state{myFilters=[{I,O}|S#state.myFilters]}). +-define(del_Filter(S,I), S#state{myFilters= + delete_obj(lists:keydelete(I, 1, S#state.myFilters), + S#state.myFilters)}). +-define(del_AllFilter(S), S#state{myFilters=[]}). +-define(set_Unconnected(S), S#state{client=undefined}). +-define(reset_RespondTo(S), S#state{respondTo=undefined}). +-define(set_RespondTo(S,F), S#state{respondTo=F}). +%% Event +-define(add_Event(S,E), catch cosNotification_eventDB: + add_event(S#state.eventDB, E, S#state.lifetFil, S#state.prioFil)). +-define(addAndGet_Event(S,E), catch cosNotification_eventDB: + add_and_get_event(S#state.eventDB, E, S#state.lifetFil, S#state.prioFil)). +%% Admin +-define(set_PacingTimer(S,T), S#state{pacingTimer=T}). +%% Subscribe +-define(add_Subscribe(S,E), ets:insert(S#state.etsR, {E, subscribe})). +-define(del_Subscribe(S,E), ets:delete(S#state.etsR, E)). +-define(set_SubscribeType(S,T), S#state{subscribeType=T}). +-define(set_SubscribeData(S,D), S#state{subscribeData=D}). +%% ID +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). + +%% MISC +-define(is_ANY(S), S#state.myType == 'PULL_ANY'). +-define(is_STRUCTURED(S), S#state.myType == 'PULL_STRUCTURED'). +-define(is_SEQUENCE(S), S#state.myType == 'PULL_SEQUENCE'). +-define(is_ANDOP(S), S#state.myOperator == 'AND_OP'). +-define(is_UnConnected(S), S#state.client == undefined). +-define(is_Connected(S), S#state.client =/= undefined). +-define(is_Waiting(S), S#state.respondTo =/= undefined). +-define(is_SubscribedFor(S,K), ets:lookup(S#state.etsR, K) =/= []). +-define(is_BatchLimitReached(S,M), cosNotification_eventDB: + status(S#state.eventDB, {batchLimit, + ?not_GetMaximumBatchSize((S#state.qosLocal)), M})). + +%%----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%----------------------------------------------------------- + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?DBG("INFO: ~p~n", [Info]), + case Info of + {'EXIT', Pid, Reason} when ?get_MyAdminPid(State)==Pid-> + ?DBG("PARENT ADMIN: ~p TERMINATED.~n",[Reason]), + {stop, Reason, State}; + {'EXIT', _Pid, _Reason} -> + ?DBG("PROXYPUSHSUPPLIER: ~p TERMINATED.~n",[_Reason]), + {noreply, State}; + {pacing, TS} when ?is_Waiting(State) -> + case ?get_PacingTimer(State) of + {_, TS} -> + ?DBG("PULL SUPPLIER PACING LIMIT REACHED~n",[]), + {RespondTo, Max} = ?get_RespondTo(State), + {EventSeq, _} = ?get_Events(State, Max), + corba:reply(RespondTo, EventSeq), + {noreply, ?reset_RespondTo(State)}; + _ -> + %% Must have been an old timer event, i.e., we reached the + %% Batch Limit before Pace limit and we were not able + %% to stop the timer before it triggered an event. + ?DBG("PULL SUPPLIER OLD PACING LIMIT REACHED~n",[]), + {noreply, State} + end; + {pacing, _} -> + ?DBG("PULL SUPPLIER PACING LIMIT REACHED BUT NO CLIENT; IMPOSSIBLE!!!~n",[]), + {noreply, State}; + _ -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([MyType, MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, Options, Operator]) -> + process_flag(trap_exit, true), + GCTime = 'CosNotification_Common':get_option(gcTime, Options, + ?not_DEFAULT_SETTINGS), + GCLimit = 'CosNotification_Common':get_option(gcLimit, Options, + ?not_DEFAULT_SETTINGS), + TimeRef = 'CosNotification_Common':get_option(timeService, Options, + ?not_DEFAULT_SETTINGS), + {ok, ?get_InitState(MyType, MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, + Operator, GCTime, GCLimit, TimeRef)}. + +terminate(_Reason, State) when ?is_UnConnected(State) -> + ok; +terminate(_Reason, State) -> + Client = ?get_Client(State), + case catch corba_object:is_nil(Client) of + false when ?is_ANY(State) -> + 'CosNotification_Common':disconnect('CosEventComm_PullConsumer', + disconnect_pull_consumer, + Client); + false when ?is_SEQUENCE(State) -> + 'CosNotification_Common':disconnect('CosNotifyComm_SequencePullConsumer', + disconnect_sequence_pull_consumer, + Client); + false when ?is_STRUCTURED(State) -> + 'CosNotification_Common':disconnect('CosNotifyComm_StructuredPullConsumer', + disconnect_structured_pull_consumer, + Client); + _ -> + ok + end. + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_ProxySupplier attributes ------ +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyType' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyType'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyType(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyAdmin' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyAdmin'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyAdmin(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_*et_priority_filter' +%% Type : read/write +%% Returns : +%%----------------------------------------------------------- +'_get_priority_filter'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PrioFil(State), State}. +'_set_priority_filter'(_OE_THIS, _OE_FROM, State, PrioF) -> + {reply, ok, ?set_PrioFil(State, PrioF)}. + + +%%----------------------------------------------------------% +%% Attribute: '_*et_lifetime_filter' +%% Type : read/write +%% Returns : +%%----------------------------------------------------------- +'_get_lifetime_filter'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_LifeTFil(State), State}. +'_set_lifetime_filter'(_OE_THIS, _OE_FROM, State, LifeTF) -> + {reply, ok, ?set_LifeTFil(State, LifeTF)}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----- CosEventChannelAdmin::ProxyPullSupplier ------------- +%%----------------------------------------------------------% +%% function : connect_pull_consumer +%% Arguments: Client - CosEventComm::PullConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%% Both exceptions from CosEventChannelAdmin!!! +%%----------------------------------------------------------- +connect_pull_consumer(OE_THIS, OE_FROM, State, Client) -> + connect_any_pull_consumer(OE_THIS, OE_FROM, State, Client). + +%%----- CosNotifyChannelAdmin::ProxyPullSupplier ------------ +%%----------------------------------------------------------% +%% function : connect_any_pull_consumer +%% Arguments: Client - CosEventComm::PullConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%% Both exceptions from CosEventChannelAdmin!!! +%%----------------------------------------------------------- +connect_any_pull_consumer(_OE_THIS, _OE_FROM, State, Client) when ?is_ANY(State) -> + ?not_TypeCheck(Client, 'CosEventComm_PullConsumer'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, ?set_Client(State, Client)} + end; +connect_any_pull_consumer(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::SequenceProxyPullSupplier ---- +%%----------------------------------------------------------% +%% function : connect_sequence_pull_consumer +%% Arguments: Client - CosNotifyComm::SequencePullConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%%----------------------------------------------------------- +connect_sequence_pull_consumer(_OE_THIS, _OE_FROM, State, Client) when ?is_SEQUENCE(State) -> + ?not_TypeCheck(Client, 'CosNotifyComm_SequencePullConsumer'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, ?set_Client(State, Client)} + end; +connect_sequence_pull_consumer(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::StructuredProxyPullSupplier -- +%%----------------------------------------------------------% +%% function : connect_structured_pull_consumer +%% Arguments: Client - CosNotifyComm::StructuredPullConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} | +%% {'EXCEPTION', #'BAD_OPERATION'{}} +%%----------------------------------------------------------- +connect_structured_pull_consumer(_OE_THIS, _OE_FROM, State, Client) when ?is_STRUCTURED(State) -> + ?not_TypeCheck(Client, 'CosNotifyComm_StructuredPullConsumer'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, ?set_Client(State, Client)} + end; +connect_structured_pull_consumer(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- Inherit from CosNotifyChannelAdmin::ProxySupplier --- +%%----------------------------------------------------------% +%% function : obtain_offered_types +%% Arguments: Mode - enum 'ObtainInfoMode' (CosNotifyChannelAdmin) +%% Returns : CosNotification::EventTypeSeq +%%----------------------------------------------------------- +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_OFF') -> + {reply, ?get_AllSubscribe(State), ?set_SubscribeType(State, false)}; +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_ON') -> + {reply, ?get_AllSubscribe(State), ?set_SubscribeType(State, true)}; +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_OFF') -> + {reply, [], ?set_SubscribeType(State, false)}; +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_ON') -> + {reply, [], ?set_SubscribeType(State, true)}; +obtain_offered_types(_,_,_,What) -> + orber:dbg("[~p] PullerSupplier:obtain_offered_types(~p);~n" + "Incorrect enumerant", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : validate_event_qos +%% Arguments: RequiredQoS - CosNotification::QoSProperties +%% Returns : ok | {'EXCEPTION', #'UnsupportedQoS'{}} +%% AvilableQoS - CosNotification::NamedPropertyRangeSeq (out) +%%----------------------------------------------------------- +validate_event_qos(_OE_THIS, _OE_FROM, State, RequiredQoS) -> + AvilableQoS = 'CosNotification_Common':validate_event_qos(RequiredQoS, + ?get_LocalQoS(State)), + {reply, {ok, AvilableQoS}, State}. + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {NewQoS, LQS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + NewState = ?update_EventDB(State, LQS), + {reply, ok, ?set_BothQoS(NewState, NewQoS, LQS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} +%% {ok, CosNotification::NamedPropertyRangeSeq} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotifyComm::NotifySubscribe --------- +%%----------------------------------------------------------% +%% function : subscription_change +%% Arguments: Added - Removed - CosNotification::EventTypeSeq +%% Returns : ok +%%----------------------------------------------------------- +subscription_change(_OE_THIS, _OE_FROM, State, Added, Removed) -> + cosNotification_Filter:validate_types(Added), + cosNotification_Filter:validate_types(Removed), + %% On this "side", we care about which type of events the client + %% will require, since the client (or an agent) clearly stated + %% that it's only interested in these types of events. + %% Also see PusherConsumer- and PullerConsumer-'offer_change'. + update_subscribe(remove, State, Removed), + CurrentSub = ?get_AllSubscribe(State), + NewState = + case cosNotification_Filter:check_types(Added++CurrentSub) of + true -> + %% Types supplied does in some way cause all events to be valid. + %% Smart? Would have been better to not supply any at all. + ?set_SubscribeData(State, true); + {ok, Which, WC} -> + ?set_SubscribeData(State, {Which, WC}) + end, + update_subscribe(add, NewState, Added), + case ?get_SubscribeType(NewState) of + true -> + %% Perhaps we should handle exception here?! + %% Probably not. Better to stay "on-line". + catch 'CosNotifyComm_NotifyPublish': + offer_change(?get_Client(NewState), Added, Removed), + ok; + _-> + ok + end, + {reply, ok, NewState}. + +update_subscribe(_, _, [])-> + ok; +update_subscribe(add, State, [H|T]) -> + ?add_Subscribe(State, H), + update_subscribe(add, State, T); +update_subscribe(remove, State, [H|T]) -> + ?del_Subscribe(State, H), + update_subscribe(remove, State, T). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ----------- +%%----------------------------------------------------------% +%% function : add_filter +%% Arguments: Filter - CosNotifyFilter::Filter +%% Returns : FilterID - long +%%----------------------------------------------------------- +add_filter(_OE_THIS, _OE_FROM, State, Filter) -> + 'CosNotification_Common':type_check(Filter, 'CosNotifyFilter_Filter'), + FilterID = ?new_Id(State), + NewState = ?set_IdCounter(State, FilterID), + {reply, FilterID, ?add_Filter(NewState, FilterID, Filter)}. + +%%----------------------------------------------------------% +%% function : remove_filter +%% Arguments: FilterID - long +%% Returns : ok +%%----------------------------------------------------------- +remove_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ok, ?del_Filter(State, FilterID)}; +remove_filter(_,_,_,What) -> + orber:dbg("[~p] PullerSupplier:remove_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_filter +%% Arguments: FilterID - long +%% Returns : Filter - CosNotifyFilter::Filter | +%% {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}} +%%----------------------------------------------------------- +get_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ?get_Filter(State, FilterID), State}; +get_filter(_,_,_,What) -> + orber:dbg("[~p] PullerSupplier:get_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_filters +%% Arguments: - +%% Returns : Filter - CosNotifyFilter::FilterIDSeq +%%----------------------------------------------------------- +get_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_AllFilterID(State), State}. + +%%----------------------------------------------------------% +%% function : remove_all_filters +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ok, ?del_AllFilter(State)}. + +%%----- Inherit from CosEventComm::PullSupplier ------------- +%%----------------------------------------------------------% +%% function : disconnect_pull_supplier +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_pull_supplier(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----------------------------------------------------------% +%% function : pull +%% Arguments: - +%% Returns : any - CORBA::ANY +%%----------------------------------------------------------- +pull(_OE_THIS, OE_FROM, State) when ?is_ANY(State) -> + case ?get_Event(State) of + {[], _} -> + {noreply, ?set_RespondTo(State, OE_FROM)}; + {Event,_} -> + {reply, Event, State} + end; +pull(_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : try_pull +%% Arguments: - +%% Returns : any - CORBA::ANY +%% HasEvent - boolean (out-type) +%%----------------------------------------------------------- +try_pull(_OE_THIS, _OE_FROM, State) when ?is_ANY(State) -> + case ?get_Event(State) of + {[], _} -> + {reply, {any:create(orber_tc:null(), null), false}, State}; + {Event, Bool} -> + {reply, {Event, Bool}, State} + end; +try_pull(_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- Inherit from CosNotifyComm::SequencePullSupplier ---- +%%----------------------------------------------------------% +%% function : disconnect_sequence_pull_supplier +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_sequence_pull_supplier(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----------------------------------------------------------% +%% function : pull_structured_events +%% Arguments: Max - long() +%% Returns : [StructuredEvent, ..] +%%----------------------------------------------------------- +pull_structured_events(_OE_THIS, OE_FROM, State, Max) when ?is_SEQUENCE(State) -> + case ?is_BatchLimitReached(State, Max) of + true -> + %% This test is not fool-proof; if Events have been stored + %% using StartTime they will still be there but we cannot + %% deliver them anyway. To solve this "problem" would cost! + %% Hence, since it works fine otherwise it will do. + case ?get_Events(State, Max) of + {[], false} -> + NewState = start_timer(State), + {noreply, ?set_RespondTo(NewState, {OE_FROM, Max})}; + {Event,_} -> + {reply, Event, State} + end; + _-> + NewState = start_timer(State), + {noreply, ?set_RespondTo(NewState, {OE_FROM, Max})} + end; +pull_structured_events(_,_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : try_pull_structured_events +%% Arguments: Max - long() +%% Returns : [StructuredEvent, ..] +%% HasEvent - Boolean() +%%----------------------------------------------------------- +try_pull_structured_events(_OE_THIS, _OE_FROM, State, Max) when ?is_SEQUENCE(State) -> + {reply, ?get_Events(State, Max), State}; +try_pull_structured_events(_,_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- Inherit from CosNotifyComm::StructuredPullSupplier -- +%%----------------------------------------------------------% +%% function : disconnect_structured_pull_supplier +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_structured_pull_supplier(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----------------------------------------------------------% +%% function : pull_structured_event +%% Arguments: - +%% Returns : +%%----------------------------------------------------------- +pull_structured_event(_OE_THIS, OE_FROM, State) when ?is_STRUCTURED(State) -> + case ?get_Event(State) of + {[], _} -> + {noreply, ?set_RespondTo(State, OE_FROM)}; + {Event,_} -> + {reply, Event, State} + end; +pull_structured_event(_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : try_pull_structured_event +%% Arguments: - +%% Returns : +%%----------------------------------------------------------- +try_pull_structured_event(_OE_THIS, _OE_FROM, State) when ?is_STRUCTURED(State) -> + case ?get_Event(State) of + {[], _} -> + {reply, + {?not_CreateSE("","","",[],[],any:create(orber_tc:null(), null)), false}, + State}; + {Event, Bool} -> + {reply, {Event, Bool}, State} + end; +try_pull_structured_event(_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj({value, {_, Obj}}) -> Obj; +find_obj(_) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}. + +find_ids(List) -> find_ids(List, []). +find_ids([], Acc) -> Acc; +find_ids([{I,_}|T], Acc) -> find_ids(T, [I|Acc]); +find_ids(_, _) -> corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +%% Delete a single object. +%% The list do not differ, i.e., no filter removed, raise exception. +delete_obj(List,List) -> corba:raise(#'CosNotifyFilter_FilterNotFound'{}); +delete_obj(List,_) -> List. + + +%%----------------------------------------------------------- +%% function : callSeq +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callSeq(_OE_THIS, OE_FROM, State, EventsIn, Status) -> + %% We should do something here, i.e., see what QoS this Object offers and + %% act accordingly. + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:validate_event(?get_SubscribeData(State), EventsIn, + ?get_AllFilter(State), + ?get_SubscribeDB(State), + Status) of + {[], _} -> + ?DBG("PROXY NOSUBSCRIPTION SEQUENCE/STRUCTURED: ~p~n",[EventsIn]), + {noreply, State}; + %% Just one event and we got a client waiting => there is no need to store + %% the event, just transform it and pass it on. + {[Event], _} when ?is_ANY(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED SEQUENCE[1]==>ANY: ~p~n",[Event]), + AnyEvent = any:create('CosNotification_StructuredEvent':tc(),Event), + case ?addAndGet_Event(State, AnyEvent) of + {[], _} -> + ?DBG("PROXY RECEIVED UNDELIVERABLE SEQUENCE[1]: ~p~n", + [Event]), + %% Cannot deliver the event at the moment; perhaps Starttime + %% set or Deadline passed. + {noreply, State}; + {PossiblyOtherEvent, _} -> + ?DBG("PROXY RECEIVED SEQUENCE[1] ~p; DELIVER: ~p~n", + [Event, PossiblyOtherEvent]), + corba:reply(?get_RespondTo(State), PossiblyOtherEvent), + {noreply, ?reset_RespondTo(State)} + end; + {[Event],_} when ?is_STRUCTURED(State), ?is_Waiting(State) -> + case ?addAndGet_Event(State, Event) of + {[], _} -> + ?DBG("PROXY RECEIVED UNDELIVERABLE SEQUENCE[1]: ~p~n", + [Event]), + %% Cannot deliver the event at the moment; perhaps Starttime + %% set or Deadline passed. + {noreply, State}; + {PossiblyOtherEvent, _} -> + ?DBG("PROXY RECEIVED SEQUENCE[1] ~p; DELIVER: ~p~n", + [Event, PossiblyOtherEvent]), + corba:reply(?get_RespondTo(State), PossiblyOtherEvent), + {noreply, ?reset_RespondTo(State)} + end; + %% A sequence of events => store them and extract the first (according to QoS) + %% event and forward it. + {Events,_} when ?is_ANY(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED SEQUENCE==>ANY: ~p~n",[Events]), + store_events(State, Events), + case ?get_Event(State) of + {[], _} -> + {noreply, State}; + {AnyEvent, _} -> + corba:reply(?get_RespondTo(State), AnyEvent), + {noreply, ?reset_RespondTo(State)} + end; + {Events, _} when ?is_STRUCTURED(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED SEQUENCE: ~p~n",[Events]), + store_events(State, Events), + case ?get_Event(State) of + {[], _} -> + {noreply, State}; + {_StrEvent, _} -> + corba:reply(?get_RespondTo(State), Events), + {noreply, ?reset_RespondTo(State)} + end; + {Events, _} when ?is_SEQUENCE(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED SEQUENCE: ~p~n",[Events]), + %% Store them first and extract Max events in QoS order. + store_events(State, Events), + {RespondTo, Max} = ?get_RespondTo(State), + case ?is_BatchLimitReached(State, Max) of + true -> + {EventSeq, _} = ?get_Events(State, Max), + corba:reply(RespondTo, EventSeq), + stop_timer(State), + {noreply, ?reset_RespondTo(State)}; + _-> + {noreply, State} + end; + %% No client waiting. Store the event(s). + {Events, _} -> + ?DBG("PROXY RECEIVED SEQUENCE: ~p~n",[Events]), + store_events(State, Events), + {noreply, State} + end. + +store_events(_State, []) -> + ok; +store_events(State, [Event|Rest]) when ?is_ANY(State) -> + AnyEvent = any:create('CosNotification_StructuredEvent':tc(),Event), + ?add_Event(State,AnyEvent), + store_events(State, Rest); +store_events(State, [Event|Rest]) -> + ?add_Event(State,Event), + store_events(State, Rest). + +%%----------------------------------------------------------- +%% function : callAny +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callAny(_OE_THIS, OE_FROM, State, EventIn, Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:validate_event(?get_SubscribeData(State), EventIn, + ?get_AllFilter(State), + ?get_SubscribeDB(State), + Status) of + {[],_} -> + ?DBG("PROXY NOSUBSCRIPTION ANY: ~p~n",[EventIn]), + {noreply, State}; + {Event,_} when ?is_ANY(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED ANY: ~p~n",[Event]), + case ?addAndGet_Event(State, Event) of + {[],_} -> + %% Unable to deliver the event (Starttime, Deadline etc). + {noreply, State}; + {MaybeOtherEvent , _} -> + corba:reply(?get_RespondTo(State), MaybeOtherEvent), + {noreply, ?reset_RespondTo(State)} + end; + {Event,_} when ?is_ANY(State) -> + ?DBG("PROXY RECEIVED ANY: ~p~n",[Event]), + ?add_Event(State,Event), + {noreply, State}; + {Event,_} when ?is_STRUCTURED(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED ANY==>STRUCTURED: ~p~n",[Event]), + case ?addAndGet_Event(State, ?not_CreateSE("","%ANY","",[],[],Event)) of + {[],_} -> + %% Unable to deliver the event (Starttime, Deadline etc). + {noreply, State}; + {MaybeOtherEvent , _} -> + corba:reply(?get_RespondTo(State), MaybeOtherEvent), + {noreply, ?reset_RespondTo(State)} + end; + {Event,_} when ?is_SEQUENCE(State), ?is_Waiting(State) -> + ?DBG("PROXY RECEIVED ANY==>SEQUENCE[1]: ~p~n",[Event]), + ?add_Event(State,?not_CreateSE("","%ANY","",[],[],Event)), + {RespondTo, Max} = ?get_RespondTo(State), + case ?is_BatchLimitReached(State, Max) of + true -> + {EventSeq, _} = ?get_Events(State, Max), + corba:reply(RespondTo, EventSeq), + stop_timer(State), + {noreply, ?reset_RespondTo(State)}; + _ -> + {noreply, State} + end; + {Event,_} -> + ?DBG("PROXY RECEIVED ANY==>STRUCTURED/SEQUENCE: ~p~n",[Event]), + ?add_Event(State,?not_CreateSE("","%ANY","",[],[],Event)), + {noreply, State} + end. + + + +%% Start timers which send a message each time we should push events. Only used +%% when this objects is defined to supply sequences. +start_timer(State) -> + TS = now(), + case catch timer:send_after(timer:seconds(?get_PacingInterval(State)), + {pacing, TS}) of + {ok,PacTRef} -> + ?DBG("PULL SUPPLIER STARTED TIMER, BATCH LIMIT: ~p~n", + [?get_BatchLimit(State)]), + ?set_PacingTimer(State, {PacTRef, TS}); + What -> + orber:dbg("[~p] PullerSupplier:start_timer();~n" + "Unable to invoke timer:send_interval/2: ~p", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +stop_timer(State) -> + case ?get_PacingTimer(State) of + undefined -> + ok; + {Timer, _} -> + ?DBG("PULL SUPPLIER STOPPED TIMER~n",[]), + timer:cancel(Timer) + end. + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/PusherConsumer_impl.erl b/lib/cosNotification/src/PusherConsumer_impl.erl new file mode 100644 index 0000000000..195e81ec58 --- /dev/null +++ b/lib/cosNotification/src/PusherConsumer_impl.erl @@ -0,0 +1,729 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : PusherConsumer_impl.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module('PusherConsumer_impl'). + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% cosEvent files. +-include_lib("cosEvent/include/CosEventChannelAdmin.hrl"). +-include_lib("cosEvent/include/CosEventComm.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::ProxyPushConsumer ------------- +-export([connect_any_push_supplier/4]). + +%%----- CosNotifyChannelAdmin::SequenceProxyPushConsumer ----- +-export([connect_sequence_push_supplier/4]). + +%%----- CosNotifyChannelAdmin::StructuredProxyPushConsumer --- +-export([connect_structured_push_supplier/4]). + +%%----- Inherit from CosNotifyChannelAdmin::ProxyConsumer ---- +-export([obtain_subscription_types/4, + validate_event_qos/4]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotifyComm::NotifyPublish ------------ +-export([offer_change/5]). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ------------ +-export([add_filter/4, + remove_filter/4, + get_filter/4, + get_all_filters/3, + remove_all_filters/3]). + +%%----- Inherit from CosEventComm::PushConsumer ------------- +-export([push/4, + disconnect_push_consumer/3]). + +%%----- Inherit from CosNotifyComm::SequencePushConsumer ---- +-export([push_structured_events/4, + disconnect_sequence_push_consumer/3]). + +%%----- Inherit from CosNotifyComm::StructuredPushConsumer -- +-export([push_structured_event/4, + disconnect_structured_push_consumer/3]). + +%%----- Inherit from CosEventChannelAdmin::ProxyPushConsumer +-export([connect_push_supplier/4]). + +%% Attributes (external) CosNotifyChannelAdmin::ProxySupplier +-export(['_get_MyType'/3, + '_get_MyAdmin'/3]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {myType, + myAdmin, + myAdminPid, + myChannel, + myFilters = [], + myOperator, + idCounter = 0, + client, + qosGlobal, + qosLocal, + publishType = false, + etsR, + eventDB}). + +%% Data structures constructors +-define(get_InitState(_MyT, _MyA, _MyAP, _QS, _LQS, _Ch, _EDB, _MyOP), + #state{myType = _MyT, + myAdmin = _MyA, + myAdminPid= _MyAP, + myChannel = _Ch, + myOperator= _MyOP, + qosGlobal = _QS, + qosLocal = _LQS, + etsR = ets:new(oe_ets, [set, protected]), + eventDB = _EDB}). + +%%-------------- Data structures selectors ----------------- +%% Attributes +-define(get_MyType(S), S#state.myType). +-define(get_MyAdmin(S), S#state.myAdmin). +-define(get_MyAdminPid(S), S#state.myAdminPid). +-define(get_MyChannel(S), S#state.myChannel). +-define(get_MyOperator(S), S#state.myOperator). +%% Client Object +-define(get_Client(S), S#state.client). +%% QoS +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +%% Filters +-define(get_Filter(S, I), find_obj(lists:keysearch(I, 1, S#state.myFilters))). +-define(get_AllFilter(S), S#state.myFilters). +-define(get_AllFilterID(S), find_ids(S#state.myFilters)). +%% Publish +-define(get_AllPublish(S), lists:flatten(ets:match(S#state.etsR, + {'$1',publish}))). +-define(get_PublishType(S), S#state.publishType). +%% ID +-define(get_IdCounter(S), S#state.idCounter). +%% Event +-define(get_Event(S), cosNotification_eventDB:get_event(S#state.eventDB)). +-define(get_Events(S,M), cosNotification_eventDB:get_events(S#state.eventDB, M)). + +-define(get_EventCounter(S), S#state.eventCounter). +%% Admin +-define(get_BatchLimit(S), ?not_GetMaximumBatchSize((S#state.qosLocal))). + +%%-------------- Data structures modifiers ----------------- +%% Client Object +-define(set_Client(S,D), S#state{client=D}). +-define(del_Client(S), S#state{client=undefined}). +-define(set_Unconnected(S), S#state{client=undefined}). +%% QoS +-define(set_LocalQoS(S,D), S#state{qosLocal=D}). +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +%% Filters +-define(add_Filter(S,I,O), S#state{myFilters=[{I,O}|S#state.myFilters]}). +-define(del_Filter(S,I), S#state{myFilters= + delete_obj(lists:keydelete(I, 1, S#state.myFilters), + S#state.myFilters)}). +-define(del_AllFilter(S), S#state{myFilters=[]}). +%% Publish +-define(add_Publish(S,E), ets:insert(S#state.etsR, {E, publish})). +-define(del_Publish(S,E), ets:delete(S#state.etsR, E)). +-define(set_PublishType(S,T), S#state{publishType=T}). +%% ID +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). +%% Event +-define(add_Event(S,E), cosNotification_eventDB:add_event(S#state.eventDB, E)). +-define(update_EventDB(S,Q), S#state{eventDB= + cosNotification_eventDB:update(S#state.eventDB, Q)}). + +-define(set_EventCounter(S,V), S#state{eventCounter=V}). +-define(add_to_EventCounter(S,V),S#state{eventCounter=S#state.eventCounter+V}). +-define(reset_EventCounter(S), S#state{eventCounter=0}). +-define(increase_EventCounter(S),S#state{eventCounter=(S#state.eventCounter+1)}). +-define(decrease_EventCounter(S),S#state{eventCounter=S#state.eventCounter-1}). +-define(add_ToEventCounter(S,A), S#state{eventCounter=(S#state.eventCounter+A)}). +-define(sub_FromEventCounter(S,_A), S#state{eventCounter=(S#state.eventCounter-_A)}). +-define(set_EventCounterTo(S,V), S#state{eventCounter=V}). + +%% MISC +-define(is_ANY(S), S#state.myType == 'PUSH_ANY'). +-define(is_STRUCTURED(S), S#state.myType == 'PUSH_STRUCTURED'). +-define(is_SEQUENCE(S), S#state.myType == 'PUSH_SEQUENCE'). +-define(is_ANDOP(S), S#state.myOperator == 'AND_OP'). +-define(is_UnConnected(S), S#state.client == undefined). +-define(is_Connected(S), S#state.client =/= undefined). +-define(is_PersistentConnection(S), + ?not_GetConnectionReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_PersistentEvent(S), + ?not_GetEventReliability((S#state.qosLocal)) == ?not_Persistent). +-define(is_BatchLimitReached(S), S#state.eventCounter >= + ?not_GetMaximumBatchSize((S#state.qosLocal))). + + +%%----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%----------------------------------------------------------- + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, State) -> + ?DBG("INFO: ~p~n", [Info]), + case Info of + {'EXIT', Pid, Reason} when ?get_MyAdminPid(State)==Pid-> + ?DBG("PARENT ADMIN: ~p TERMINATED.~n",[Reason]), + {stop, Reason, State}; + {'EXIT', _Pid, _Reason} -> + ?DBG("PROXYPUSHCONSUMER: ~p TERMINATED.~n",[_Reason]), + {noreply, State}; + _ -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init(['PUSH_SEQUENCE', MyAdmin, MyAdminPid, InitQoS, LQS, + MyChannel, Options, Operator]) -> + process_flag(trap_exit, true), + %% Only if MyType is 'PUSH_SEQUENCE' we need an ets to store events in. + %% Otherwise we'll forward them at once. Why? We don't know when the next event + %% is due. + GCTime = 'CosNotification_Common':get_option(gcTime, Options, + ?not_DEFAULT_SETTINGS), + GCLimit = 'CosNotification_Common':get_option(gcLimit, Options, + ?not_DEFAULT_SETTINGS), + TimeRef = 'CosNotification_Common':get_option(timeService, Options, + ?not_DEFAULT_SETTINGS), + {ok, ?get_InitState('PUSH_SEQUENCE', MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, + cosNotification_eventDB:create_db(LQS, GCTime, GCLimit, TimeRef), + Operator)}; +init([MyType, MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, _Options, Operator]) -> + process_flag(trap_exit, true), + {ok, ?get_InitState(MyType, MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, + undefined, Operator)}. + +terminate(_Reason, State) when ?is_UnConnected(State) -> + ok; +terminate(_Reason, State) -> + Client = ?get_Client(State), + case catch corba_object:is_nil(Client) of + false when ?is_ANY(State) -> + 'CosNotification_Common':disconnect('CosEventComm_PushSupplier', + disconnect_push_supplier, + Client); + false when ?is_SEQUENCE(State) -> + 'CosNotification_Common':disconnect('CosNotifyComm_SequencePushSupplier', + disconnect_sequence_push_supplier, + Client); + false when ?is_STRUCTURED(State) -> + 'CosNotification_Common':disconnect('CosNotifyComm_StructuredPushSupplier', + disconnect_structured_push_supplier, + Client); + _ -> + ok + end. + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_ProxyConsumer attributes ------ +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyType' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyType'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyType(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyAdmin' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyAdmin'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyAdmin(State), State}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----- CosEventChannelAdmin::ProxyPushConsumer ------------- +%%----------------------------------------------------------% +%% function : connect_push_supplier +%% Arguments: Client - CosEventComm::PushSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%% Both exceptions from CosEventChannelAdmin!!! +%%----------------------------------------------------------- +connect_push_supplier(OE_THIS, OE_FROM, State, Client) -> + connect_any_push_supplier(OE_THIS, OE_FROM, State, Client). + +%%----- CosNotifyChannelAdmin::ProxyPushConsumer ------------ +%%----------------------------------------------------------% +%% function : connect_any_push_supplier +%% Arguments: Client - CosEventComm::PushSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%% Both exceptions from CosEventChannelAdmin!!! +%%----------------------------------------------------------- +connect_any_push_supplier(_OE_THIS, _OE_FROM, State, Client) when ?is_ANY(State) -> + ?not_TypeCheck(Client, 'CosEventComm_PushSupplier'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, ?set_Client(State, Client)} + end; +connect_any_push_supplier(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::SequenceProxyPushConsumer ---- +%%----------------------------------------------------------% +%% function : connect_sequence_push_supplier +%% Arguments: Client - CosNotifyComm::SequencePushSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%%----------------------------------------------------------- +connect_sequence_push_supplier(_OE_THIS, _OE_FROM, State, Client) when ?is_SEQUENCE(State) -> + ?not_TypeCheck(Client, 'CosNotifyComm_SequencePushSupplier'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, ?set_Client(State, Client)} + end; +connect_sequence_push_supplier(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::StructuredProxyPushConsumer -- +%%----------------------------------------------------------% +%% function : connect_structured_push_supplier +%% Arguments: Client - CosNotifyComm::StructuredPushSupplier +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%%----------------------------------------------------------- +connect_structured_push_supplier(_OE_THIS, _OE_FROM, State, Client) when ?is_STRUCTURED(State) -> + ?not_TypeCheck(Client, 'CosNotifyComm_StructuredPushSupplier'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, ?set_Client(State, Client)} + end; +connect_structured_push_supplier(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- Inherit from CosNotifyChannelAdmin::ProxyConsumer --- +%%----------------------------------------------------------% +%% function : obtain_subscription_types +%% Arguments: Mode - enum 'ObtainInfoMode' (CosNotifyChannelAdmin) +%% Returns : CosNotification::EventTypeSeq +%%----------------------------------------------------------- +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_OFF') -> + {reply, ?get_AllPublish(State), ?set_PublishType(State, false)}; +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_ON') -> + {reply, ?get_AllPublish(State), ?set_PublishType(State, true)}; +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_OFF') -> + {reply, [], ?set_PublishType(State, false)}; +obtain_subscription_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_ON') -> + {reply, [], ?set_PublishType(State, true)}; +obtain_subscription_types(_,_,_,What) -> + orber:dbg("[~p] PusherConsumer:obtain_subscription_types(~p);~n" + "Incorrect enumerant", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : validate_event_qos +%% Arguments: RequiredQoS - CosNotification::QoSProperties +%% Returns : ok | {'EXCEPTION', #'UnsupportedQoS'{}} +%% AvilableQoS - CosNotification::NamedPropertyRangeSeq (out) +%%----------------------------------------------------------- +validate_event_qos(_OE_THIS, _OE_FROM, State, RequiredQoS) -> + AvilableQoS = 'CosNotification_Common':validate_event_qos(RequiredQoS, + ?get_LocalQoS(State)), + {reply, {ok, AvilableQoS}, State}. + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {NewQoS, LQS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + NewState = ?update_EventDB(State, LQS), + {reply, ok, ?set_BothQoS(NewState, NewQoS, LQS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} +%% {ok, CosNotification::NamedPropertyRangeSeq} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotifyComm::NotifyPublish ----------- +%%----------------------------------------------------------% +%% function : offer_change +%% Arguments: Added - #'CosNotification_EventType'{} +%% Removed - #'CosNotification_EventType'{} +%% Returns : ok | +%% {'EXCEPTION', #'CosNotifyComm_InvalidEventType'{}} +%%----------------------------------------------------------- +offer_change(_OE_THIS, _OE_FROM, State, Added, Removed) -> + cosNotification_Filter:validate_types(Added), + cosNotification_Filter:validate_types(Removed), + %% On this "side" we don't really care about which + %% type of events the client will supply. + %% Perhaps, later on, if we want to check this against Filters + %% associated with this object we may change this approach, i.e., if + %% the filter will not allow passing certain event types. But the + %% user should see to that that situation never occurs. It would add + %% extra overhead. Also see PusherSupplier- and PullerSuppler- + %% 'subscription_change'. + update_publish(add, State, Added), + update_publish(remove, State, Removed), + case ?get_PublishType(State) of + true -> + %% Perhaps we should handle exception here?! + %% Probably not. Better to stay "on-line". + catch 'CosNotifyComm_NotifySubscribe': + subscription_change(?get_Client(State), Added, Removed), + ok; + _-> + ok + end, + {reply, ok, State}. + +update_publish(_, _, [])-> + ok; +update_publish(add, State, [H|T]) -> + ?add_Publish(State, H), + update_publish(add, State, T); +update_publish(remove, State, [H|T]) -> + ?del_Publish(State, H), + update_publish(remove, State, T). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ----------- +%%----------------------------------------------------------% +%% function : add_filter +%% Arguments: Filter - CosNotifyFilter::Filter +%% Returns : FilterID - long +%%----------------------------------------------------------- +add_filter(_OE_THIS, _OE_FROM, State, Filter) -> + 'CosNotification_Common':type_check(Filter, 'CosNotifyFilter_Filter'), + FilterID = ?new_Id(State), + NewState = ?set_IdCounter(State, FilterID), + {reply, FilterID, ?add_Filter(NewState, FilterID, Filter)}. + +%%----------------------------------------------------------% +%% function : remove_filter +%% Arguments: FilterID - long +%% Returns : ok +%%----------------------------------------------------------- +remove_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ok, ?del_Filter(State, FilterID)}; +remove_filter(_,_,_,What) -> + orber:dbg("[~p] PusherConsumer:remove_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_filter +%% Arguments: FilterID - long +%% Returns : Filter - CosNotifyFilter::Filter | +%% {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}} +%%----------------------------------------------------------- +get_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ?get_Filter(State, FilterID), State}; +get_filter(_,_,_,What) -> + orber:dbg("[~p] PusherConsumer:get_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_filters +%% Arguments: - +%% Returns : Filter - CosNotifyFilter::FilterIDSeq +%%----------------------------------------------------------- +get_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_AllFilterID(State), State}. + +%%----------------------------------------------------------% +%% function : remove_all_filters +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ok, ?del_AllFilter(State)}. + +%%----- Inherit from CosEventComm::PushConsumer ------------- +%%----------------------------------------------------------% +%% function : disconnect_push_consumer +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_push_consumer(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----------------------------------------------------------% +%% function : push +%% Arguments: AnyEvent +%% Returns : ok | +%%----------------------------------------------------------- +push(OE_THIS, OE_FROM, State, Event) when ?is_ANY(State) -> + corba:reply(OE_FROM, ok), + case {?not_isConvertedStructured(Event), + cosNotification_eventDB:filter_events([Event], ?get_AllFilter(State))} of + {_, {[],_}} when ?is_ANDOP(State) -> + {noreply, State}; + {true, {[],[_]}} -> + %% Is OR and converted, change back and forward to Admin + forward(seq, ?get_MyAdmin(State), State, [any:get_value(Event)], + 'MATCH', OE_THIS); + {_, {[],[_]}} -> + %% Is OR and not converted, forward to Admin + forward(any, ?get_MyAdmin(State), State, Event, 'MATCH', OE_THIS); + {true, {[_],_}} when ?is_ANDOP(State) -> + %% Is AND and converted, change back and forward to Admin + forward(seq, ?get_MyAdmin(State), State, [any:get_value(Event)], + 'MATCH', OE_THIS); + {true, {[_],_}} -> + %% Is OR and converted, change back and forward to Channel + forward(seq, ?get_MyChannel(State), State, [any:get_value(Event)], + 'MATCHED', OE_THIS); + {_, {[_],_}} when ?is_ANDOP(State) -> + %% Is AND and not converted, forward to Admin + forward(any, ?get_MyAdmin(State), State, Event, 'MATCH', OE_THIS); + _ -> + %% Is OR and not converted, forward to Channel + forward(any, ?get_MyChannel(State), State, Event, 'MATCHED', OE_THIS) + end; +push(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- Inherit from CosNotifyComm::SequencePushConsumer ---- +%%----------------------------------------------------------% +%% function : disconnect_sequence_push_consumer +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_sequence_push_consumer(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----------------------------------------------------------% +%% function : push_structured_events +%% Arguments: CosNotification::EventBatch +%% Returns : ok | +%%----------------------------------------------------------- +push_structured_events(OE_THIS, OE_FROM, State, Events) when ?is_SEQUENCE(State) -> + corba:reply(OE_FROM, ok), + %% We cannot convert parts of the sequence to any, event though they + %% are converted from any to structured. Would be 'impossible' to send. + case cosNotification_eventDB:filter_events(Events, ?get_AllFilter(State)) of + {[],_} when ?is_ANDOP(State) -> + {noreply, State}; + {[],Failed} -> + forward(seq, ?get_MyAdmin(State), State, Failed, 'MATCH', OE_THIS); + {Passed, _} when ?is_ANDOP(State) -> + forward(seq, ?get_MyAdmin(State), State, Passed, 'MATCH', OE_THIS); + {Passed, []} -> + forward(seq, ?get_MyChannel(State), State, Passed, 'MATCHED', OE_THIS); + {Passed, Failed} -> + %% Is OR, send Passed to channel and Failed to Admin. + forward(seq, ?get_MyChannel(State), State, Passed, 'MATCHED', OE_THIS), + forward(seq, ?get_MyAdmin(State), State, Failed, 'MATCH', OE_THIS) + end; +push_structured_events(_,_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- Inherit from CosNotifyComm::StructuredPushConsumer -- +%%----------------------------------------------------------% +%% function : disconnect_structured_push_consumer +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_structured_push_consumer(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----------------------------------------------------------% +%% function : push_structured_event +%% Arguments: CosNotification::StructuredEvent +%% Returns : ok | +%%----------------------------------------------------------- +push_structured_event(OE_THIS, OE_FROM, State, Event) when ?is_STRUCTURED(State) -> + corba:reply(OE_FROM, ok), + case {?not_isConvertedAny(Event), + cosNotification_eventDB:filter_events([Event], ?get_AllFilter(State))} of + {_, {[],_}} when ?is_ANDOP(State) -> + {noreply, State}; + {true, {[],[_]}} -> + %% Is OR and converted, change back and forward to Admin + forward(any, ?get_MyAdmin(State), State, + Event#'CosNotification_StructuredEvent'.remainder_of_body, + 'MATCH', OE_THIS); + {_, {[],[_]}} -> + %% Is OR and not converted, forward to Admin + forward(seq, ?get_MyAdmin(State), State, [Event], 'MATCH', OE_THIS); + {true, {[_],_}} when ?is_ANDOP(State) -> + %% Is AND and converted, change back and forward to Admin + forward(any, ?get_MyAdmin(State), State, + Event#'CosNotification_StructuredEvent'.remainder_of_body, + 'MATCH', OE_THIS); + {true, {[_],_}} -> + %% Is OR and converted, change back and forward to Channel + forward(any, ?get_MyChannel(State), State, + Event#'CosNotification_StructuredEvent'.remainder_of_body, + 'MATCHED', OE_THIS); + {_, {[_],_}} when ?is_ANDOP(State) -> + %% Is AND and not converted, forward to Admin + forward(seq, ?get_MyAdmin(State), State, [Event], 'MATCH', OE_THIS); + _ -> + %% Is OR and not converted, forward to Channel + forward(seq, ?get_MyChannel(State), State, [Event], 'MATCHED', OE_THIS) + end; +push_structured_event(_,_,_,_) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj({value, {_, Obj}}) -> Obj; +find_obj(_) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}. + +find_ids(List) -> find_ids(List, []). +find_ids([], Acc) -> Acc; +find_ids([{I,_}|T], Acc) -> find_ids(T, [I|Acc]); +find_ids(What, _) -> + orber:dbg("[~p] PusherConsumer:find_ids();~n" + "Id corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +%% Delete a single object. +%% The list don not differ, i.e., no filter removed, raise exception. +delete_obj(List,List) -> corba:raise(#'CosNotifyFilter_FilterNotFound'{}); +delete_obj(List,_) -> List. + +%% Forward events +forward(any, SendTo, State, Event, Status, OE_THIS) -> + case catch oe_CosNotificationComm_Event:callAny(SendTo, Event, Status) of + ok -> + ?DBG("PROXY FORWARD ANY: ~p~n",[Event]), + {noreply, State}; + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + orber:dbg("[~p] PusherConsumer:forward();~n" + "Admin/Channel no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, OE_THIS}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] PusherConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Dropping: ~p", [?LINE, R, Event], ?DEBUG_LEVEL), + {noreply, State}; + R -> + orber:dbg("[~p] PusherConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, R, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, OE_THIS}, + {client, ?get_Client(State)}, + {reason, R}]), + {stop, normal, State} + end; +forward(seq, SendTo, State, Event, Status, OE_THIS) -> + case catch oe_CosNotificationComm_Event:callSeq(SendTo, Event, Status) of + ok -> + {noreply, State}; + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + ?DBG("ADMIN NO LONGER EXIST; DROPPING: ~p~n", [Event]), + 'CosNotification_Common':notify([{proxy, OE_THIS}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + R when ?is_PersistentConnection(State) -> + orber:dbg("[~p] PusherConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Dropping: ~p", [?LINE, R, Event], ?DEBUG_LEVEL), + {noreply, State}; + R -> + orber:dbg("[~p] PusherConsumer:forward();~n" + "Admin/Channel respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, R, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, OE_THIS}, + {client, ?get_Client(State)}, + {reason, R}]), + {stop, normal, State} + end. + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/PusherSupplier_impl.erl b/lib/cosNotification/src/PusherSupplier_impl.erl new file mode 100644 index 0000000000..51949b8c46 --- /dev/null +++ b/lib/cosNotification/src/PusherSupplier_impl.erl @@ -0,0 +1,1052 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : PusherSupplier_impl.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module('PusherSupplier_impl'). + + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% cosEvent files. +-include_lib("cosEvent/include/CosEventChannelAdmin.hrl"). +-include_lib("cosEvent/include/CosEventComm.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%%--------------- External ----------------------------------- +%%----- CosNotifyChannelAdmin::ProxyPushSupplier ------------- +-export([connect_any_push_consumer/4]). + +%%----- CosNotifyChannelAdmin::StructuredProxyPushSupplier --- +-export([connect_structured_push_consumer/4]). + +%%----- CosNotifyChannelAdmin::SequenceProxyPushSupplier ----- +-export([connect_sequence_push_consumer/4]). + +%%----- CosNotifyChannelAdmin::*ProxyPushSupplier ------------ +-export([suspend_connection/3, + resume_connection/3]). + +%%----- Inherit from CosNotifyChannelAdmin::ProxySupplier ---- +-export([obtain_offered_types/4, + validate_event_qos/4]). + +%%----- Inherit from CosNotification::QoSAdmin --------------- +-export([get_qos/3, + set_qos/4, + validate_qos/4]). + +%%----- Inherit from CosNotifyComm::NotifySubscribe ---------- +-export([subscription_change/5]). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ------------ +-export([add_filter/4, + remove_filter/4, + get_filter/4, + get_all_filters/3, + remove_all_filters/3]). + +%%----- Inherit from CosEventComm::PushSupplier ------------- +-export([disconnect_push_supplier/3]). + +%%----- Inherit from CosNotifyComm::StructuredPushSupplier -- +-export([disconnect_structured_push_supplier/3]). + +%%----- Inherit from CosNotifyComm::SequencePushSupplier ---- +-export([disconnect_sequence_push_supplier/3]). + +%%----- Inherit from CosEventChannelAdmin::ProxyPushSupplier +-export([connect_push_consumer/4]). + +%% Attributes (external) CosNotifyChannelAdmin::ProxySupplier +-export(['_get_MyType'/3, + '_get_MyAdmin'/3, + '_get_priority_filter'/3, + '_set_priority_filter'/4, + '_get_lifetime_filter'/3, + '_set_lifetime_filter'/4]). + +%%--------------- Internal ----------------------------------- +%%----- Inherit from cosNotificationComm --------------------- +-export([callAny/5, + callSeq/5]). + +%%--------------- gen_server specific exports ---------------- +-export([handle_info/2, code_change/3]). +-export([init/1, terminate/2]). + +%%--------------- LOCAL DEFINITIONS -------------------------- +%% Data structures +-record(state, {myType, + myAdmin, + myAdminPid, + myChannel, + myFilters = [], + myOperator, + idCounter = 0, + prioFil, + lifetFil, + client, + qosGlobal, + qosLocal, + suspended = false, + pacingTimer, + subscribeType = false, + subscribeData = true, + etsR, + eventDB, + this, + maxCache, + cacheTimeout, + cacheInterval}). + +%% Data structures constructors +-define(get_InitState(_MyT, _MyA, _MyAP, _QS, _LQS, _Ch, _MyOp, _GT, _GL, _TR), + #state{myType = _MyT, + myAdmin = _MyA, + myAdminPid= _MyAP, + qosGlobal = _QS, + qosLocal = _LQS, + myChannel = _Ch, + myOperator=_MyOp, + etsR = ets:new(oe_ets, [set, protected]), + eventDB = cosNotification_eventDB:create_db(_LQS, _GT, _GL, _TR), + maxCache = cosNotificationApp:max_events()}). + +%% Data structures selectors +%%-------------- Data structures selectors ----------------- +%% Attributes +-define(get_MyType(S), S#state.myType). +-define(get_MyAdmin(S), S#state.myAdmin). +-define(get_MyAdminPid(S), S#state.myAdminPid). +-define(get_MyChannel(S), S#state.myChannel). +-define(get_MyOperator(S), S#state.myOperator). +-define(get_PrioFil(S), S#state.prioFil). +-define(get_LifeTFil(S), S#state.lifetFil). +%% Client Object +-define(get_Client(S), S#state.client). +%% QoS +-define(get_GlobalQoS(S), S#state.qosGlobal). +-define(get_LocalQoS(S), S#state.qosLocal). +-define(get_BothQoS(S), {S#state.qosGlobal, S#state.qosLocal}). +%% Filters +-define(get_Filter(S, I), find_obj(lists:keysearch(I, 1, S#state.myFilters))). +-define(get_AllFilter(S), S#state.myFilters). +-define(get_AllFilterID(S), find_ids(S#state.myFilters)). +%% Amin +-define(get_PacingTimer(S), S#state.pacingTimer). +-define(get_PacingInterval(S), round(?not_GetPacingInterval((S#state.qosLocal))/10000000)). +-define(get_BatchLimit(S), ?not_GetMaximumBatchSize((S#state.qosLocal))). +%% Subscribe +-define(get_AllSubscribe(S), lists:flatten(ets:match(S#state.etsR, + {'$1',subscribe}))). +-define(get_SubscribeType(S), S#state.subscribeType). +-define(get_SubscribeData(S), S#state.subscribeData). +%% ID +-define(get_IdCounter(S), S#state.idCounter). +-define(get_SubscribeDB(S), S#state.etsR). +%% Event +-define(is_PersistentConnection(S), + (?not_GetConnectionReliability((S#state.qosLocal)) == ?not_Persistent)). +-define(is_PersistentEvent(S), + (?not_GetEventReliability((S#state.qosLocal)) == ?not_Persistent)). + +-define(get_Event(S), cosNotification_eventDB:get_event(S#state.eventDB, + false)). +% (not ?is_PersistentEvent(S)))). +-define(get_Events(S,M), cosNotification_eventDB:get_events(S#state.eventDB, M, false)). +% (not ?is_PersistentEvent(S)))). + +%%-------------- Data structures modifiers ----------------- +%% Attributes +-define(set_PrioFil(S,D), S#state{prioFil=D}). +-define(set_LifeTFil(S,D), S#state{lifetFil=D}). +%% Client Object +-define(set_Client(S,D), S#state{client=D}). +-define(del_Client(S), S#state{client=undefined}). +-define(set_Unconnected(S), S#state{client=undefined}). +-define(set_Suspended(S), S#state{suspended=true}). +-define(set_NotSuspended(S), S#state{suspended=false}). +%% QoS +-define(set_LocalQoS(S,D), S#state{qosLocal=D}). +-define(set_GlobalQoS(S,D), S#state{qosGlobal=D}). +-define(set_BothQoS(S,GD,LD), S#state{qosGlobal=GD, qosLocal=LD}). +%% Filters +-define(add_Filter(S,I,O), S#state{myFilters=[{I,O}|S#state.myFilters]}). +-define(del_Filter(S,I), S#state{myFilters= + delete_obj(lists:keydelete(I, 1, S#state.myFilters), + S#state.myFilters)}). +-define(del_AllFilter(S), S#state{myFilters=[]}). +%% Admin +-define(set_PacingTimer(S,T), S#state{pacingTimer=T}). +%% Publish +%% Subscribe +-define(add_Subscribe(S,E), ets:insert(S#state.etsR, {E, subscribe})). +-define(del_Subscribe(S,E), ets:delete(S#state.etsR, E)). +-define(set_SubscribeType(S,T), S#state{subscribeType=T}). +-define(set_SubscribeData(S,D), S#state{subscribeData=D}). +%% ID +-define(set_IdCounter(S,V), S#state{idCounter=V}). +-define(new_Id(S), 'CosNotification_Common':create_id(S#state.idCounter)). +%% Events +-define(add_Event(S,E), catch cosNotification_eventDB: + add_event(S#state.eventDB, E, S#state.lifetFil, S#state.prioFil)). +-define(addAndGet_Event(S,E), catch cosNotification_eventDB: + add_and_get_event(S#state.eventDB, E, S#state.lifetFil, S#state.prioFil, + false)). +% ?is_PersistentEvent(S))). +-define(update_EventDB(S,Q), S#state{eventDB= + cosNotification_eventDB:update(S#state.eventDB, Q)}). + + +%%-------------- MISC ---------------------------------------- +-define(is_ANY(S), S#state.myType == 'PUSH_ANY'). +-define(is_STRUCTURED(S), S#state.myType == 'PUSH_STRUCTURED'). +-define(is_SEQUENCE(S), S#state.myType == 'PUSH_SEQUENCE'). +-define(is_ANDOP(S), S#state.myOperator == 'AND_OP'). +-define(is_UnConnected(S), S#state.client == undefined). +-define(is_Connected(S), S#state.client =/= undefined). +-define(is_Suspended(S), S#state.suspended == true). +-define(is_NotSuspended(S), S#state.suspended == false). +-define(is_BatchLimitReached(S), cosNotification_eventDB:status(S#state.eventDB, + {batchLimit, + ?not_GetMaximumBatchSize((S#state.qosLocal))})). +-define(has_Filters(S), S#state.myFilters =/= []). + +%%----------------------------------------------------------% +%% function : handle_info, code_change +%% Arguments: +%% Returns : +%% Effect : Functions demanded by the gen_server module. +%%----------------------------------------------------------- + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +handle_info(Info, #state{cacheTimeout = Timeout, + cacheInterval = Interval} = State) -> + ?DBG("INFO: ~p~n", [Info]), + case Info of + {'EXIT', Pid, Reason} when ?get_MyAdminPid(State)==Pid -> + ?DBG("PARENT ADMIN: ~p TERMINATED.~n",[Reason]), + {stop, Reason, State}; + {'EXIT', _Pid, _Reason} -> + ?DBG("PROXYPUSHSUPPLIER: ~p TERMINATED.~n",[_Reason]), + {noreply, State}; + pacing -> + lookup_and_push(State, true); + cacheInterval -> + lookup_and_push(State, true); + cacheTimeout when Timeout == undefined, Interval == undefined -> + %% Late message, do not terminate + {noreply, State}; + cacheTimeout -> + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, State#state.client}, + {reason, + {timer, "Reached upper limit"}}]), + {stop, normal, State}; + _ -> + {noreply, State} + end. + +%%----------------------------------------------------------% +%% function : init, terminate +%% Arguments: +%%----------------------------------------------------------- + +init([MyType, MyAdmin, MyAdminPid, InitQoS, LQS, MyChannel, Options, Operator]) -> + process_flag(trap_exit, true), + GCTime = 'CosNotification_Common':get_option(gcTime, Options, + ?not_DEFAULT_SETTINGS), + GCLimit = 'CosNotification_Common':get_option(gcLimit, Options, + ?not_DEFAULT_SETTINGS), + TimeRef = 'CosNotification_Common':get_option(timeService, Options, + ?not_DEFAULT_SETTINGS), + timer:start(), + {ok, ?get_InitState(MyType, MyAdmin, MyAdminPid, + InitQoS, LQS, MyChannel, Operator, GCTime, GCLimit, TimeRef)}. + +terminate(_Reason, State) when ?is_UnConnected(State) -> + stop_timer(State#state.cacheTimeout), + stop_timer(State#state.cacheInterval), + stop_timer(State#state.pacingTimer), + %% We are not connected to a Client. Hence, no need to invoke disconnect. + ok; +terminate(_Reason, State) when ?is_ANY(State) -> + stop_timer(State#state.cacheTimeout), + stop_timer(State#state.cacheInterval), + stop_timer(State#state.pacingTimer), + 'CosNotification_Common':disconnect('CosEventComm_PushConsumer', + disconnect_push_consumer, + ?get_Client(State)); +terminate(_Reason, State) when ?is_SEQUENCE(State) -> + stop_timer(State#state.cacheTimeout), + stop_timer(State#state.cacheInterval), + stop_timer(State#state.pacingTimer), + 'CosNotification_Common':disconnect('CosNotifyComm_SequencePushConsumer', + disconnect_sequence_push_consumer, + ?get_Client(State)); +terminate(_Reason, State) when ?is_STRUCTURED(State) -> + stop_timer(State#state.cacheTimeout), + stop_timer(State#state.cacheInterval), + stop_timer(State#state.pacingTimer), + 'CosNotification_Common':disconnect('CosNotifyComm_StructuredPushConsumer', + disconnect_structured_push_consumer, + ?get_Client(State)). + +%%----------------------------------------------------------- +%%----- CosNotifyChannelAdmin_ProxySupplier attributes ------ +%%----------------------------------------------------------- +%%----------------------------------------------------------% +%% Attribute: '_get_MyType' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyType'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyType(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_get_MyAdmin' +%% Type : readonly +%% Returns : +%%----------------------------------------------------------- +'_get_MyAdmin'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_MyAdmin(State), State}. + +%%----------------------------------------------------------% +%% Attribute: '_*et_priority_filter' +%% Type : read/write +%% Returns : +%%----------------------------------------------------------- +'_get_priority_filter'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_PrioFil(State), State}. +'_set_priority_filter'(_OE_THIS, _OE_FROM, State, PrioF) -> + {reply, ok, ?set_PrioFil(State, PrioF)}. + +%%----------------------------------------------------------% +%% Attribute: '_*et_lifetime_filter' +%% Type : read/write +%% Returns : +%%----------------------------------------------------------- +'_get_lifetime_filter'(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_LifeTFil(State), State}. +'_set_lifetime_filter'(_OE_THIS, _OE_FROM, State, LifeTF) -> + {reply, ok, ?set_LifeTFil(State, LifeTF)}. + +%%----------------------------------------------------------- +%%------- Exported external functions ----------------------- +%%----------------------------------------------------------- +%%----- CosEventChannelAdmin::ProxyPushSupplier ------------- +%%----------------------------------------------------------% +%% function : connect_push_consumer +%% Arguments: Client - CosEventComm::PushConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%% Both exceptions from CosEventChannelAdmin!!!! +%%----------------------------------------------------------- +connect_push_consumer(OE_THIS, OE_FROM, State, Client) -> + connect_any_push_consumer(OE_THIS, OE_FROM, State, Client). + +%%----- CosNotifyChannelAdmin::ProxyPushSupplier ------------ +%%----------------------------------------------------------% +%% function : connect_any_push_consumer +%% Arguments: Client - CosEventComm::PushConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%% Both exceptions from CosEventChannelAdmin!!!! +%%----------------------------------------------------------- +connect_any_push_consumer(OE_THIS, _OE_FROM, State, Client) when ?is_ANY(State) -> + 'CosNotification_Common':type_check(Client, 'CosEventComm_PushConsumer'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, State#state{client = Client, this = OE_THIS}} + end; +connect_any_push_consumer(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::SequenceProxyPushSupplier ---- +%%----------------------------------------------------------% +%% function : connect_sequence_push_consumer +%% Arguments: Client - CosNotifyComm::SequencePushConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%%----------------------------------------------------------- +connect_sequence_push_consumer(OE_THIS, _OE_FROM, State, Client) when ?is_SEQUENCE(State) -> + 'CosNotification_Common':type_check(Client, + 'CosNotifyComm_SequencePushConsumer'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + NewState = start_timer(State), + {reply, ok, NewState#state{client = Client, this = OE_THIS}} + end; +connect_sequence_push_consumer(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::StructuredProxyPushSupplier --- +%%----------------------------------------------------------% +%% function : connect_structured_push_consumer +%% Arguments: Client - CosNotifyComm::StructuredPushConsumer +%% Returns : ok | {'EXCEPTION', #'AlreadyConnected'{}} | +%% {'EXCEPTION', #'TypeError'{}} +%%----------------------------------------------------------- +connect_structured_push_consumer(OE_THIS, _OE_FROM, State, Client) when ?is_STRUCTURED(State) -> + 'CosNotification_Common':type_check(Client, + 'CosNotifyComm_StructuredPushConsumer'), + if + ?is_Connected(State) -> + corba:raise(#'CosEventChannelAdmin_AlreadyConnected'{}); + true -> + {reply, ok, State#state{client = Client, this = OE_THIS}} + end; +connect_structured_push_consumer(_, _, _, _) -> + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----- CosNotifyChannelAdmin::*ProxyPushSupplier ----------- +%%----------------------------------------------------------% +%% function : suspend_connection +%% Arguments: +%% Returns : ok | {'EXCEPTION', #'ConnectionAlreadyInactive'{}} | +%% {'EXCEPTION', #'NotConneced'{}} +%%----------------------------------------------------------- +suspend_connection(_OE_THIS, _OE_FROM, State) when ?is_Connected(State) -> + if + ?is_Suspended(State) -> + corba:raise(#'CosNotifyChannelAdmin_ConnectionAlreadyInactive'{}); + true -> + stop_timer(State#state.pacingTimer), + {reply, ok, State#state{pacingTimer = undefined, + suspended=true}} + end; +suspend_connection(_,_,_)-> + corba:raise(#'CosNotifyChannelAdmin_NotConnected'{}). + +%%----------------------------------------------------------% +%% function : resume_connection +%% Arguments: +%% Returns : ok | {'EXCEPTION', #'ConnectionAlreadyActive'{}} | +%% {'EXCEPTION', #'NotConneced'{}} +%%----------------------------------------------------------- +resume_connection(_OE_THIS, OE_FROM, State) when ?is_Connected(State) -> + if + ?is_NotSuspended(State) -> + corba:raise(#'CosNotifyChannelAdmin_ConnectionAlreadyActive'{}); + true -> + corba:reply(OE_FROM, ok), + if + ?is_SEQUENCE(State) -> + start_timer(State); + true -> + ok + end, + lookup_and_push(?set_NotSuspended(State)) + end; +resume_connection(_,_,_) -> + corba:raise(#'CosNotifyChannelAdmin_NotConnected'{}). + +%%----- Inherit from CosNotifyChannelAdmin::ProxySupplier --- +%%----------------------------------------------------------% +%% function : obtain_offered_types +%% Arguments: Mode - enum 'ObtainInfoMode' (CosNotifyChannelAdmin) +%% Returns : CosNotification::EventTypeSeq +%%----------------------------------------------------------- +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_OFF') -> + {reply, ?get_AllSubscribe(State), ?set_SubscribeType(State, false)}; +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'ALL_NOW_UPDATES_ON') -> + {reply, ?get_AllSubscribe(State), ?set_SubscribeType(State, true)}; +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_OFF') -> + {reply, [], ?set_SubscribeType(State, false)}; +obtain_offered_types(_OE_THIS, _OE_FROM, State, 'NONE_NOW_UPDATES_ON') -> + {reply, [], ?set_SubscribeType(State, true)}; +obtain_offered_types(_,_,_,What) -> + orber:dbg("[~p] PusherSupplier:obtain_offered_types(~p);~n" + "Bad enumerant", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_OPERATION'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : validate_event_qos +%% Arguments: RequiredQoS - CosNotification::QoSProperties +%% Returns : ok | {'EXCEPTION', #'UnsupportedQoS'{}} +%% AvilableQoS - CosNotification::NamedPropertyRangeSeq (out) +%%----------------------------------------------------------- +validate_event_qos(_OE_THIS, _OE_FROM, State, RequiredQoS) -> + AvilableQoS = 'CosNotification_Common':validate_event_qos(RequiredQoS, + ?get_LocalQoS(State)), + {reply, {ok, AvilableQoS}, State}. + +%%----- Inherit from CosNotification::QoSAdmin -------------- +%%----------------------------------------------------------% +%% function : get_qos +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +get_qos(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_GlobalQoS(State), State}. + +%%----------------------------------------------------------% +%% function : set_qos +%% Arguments: QoS - CosNotification::QoSProperties, i.e., +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : ok | {'EXCEPTION', CosNotification::UnsupportedQoS} +%%----------------------------------------------------------- +set_qos(_OE_THIS, _OE_FROM, State, QoS) -> + {NewQoS, LQS} = 'CosNotification_Common':set_qos(QoS, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + NewState = ?update_EventDB(State, LQS), + {reply, ok, ?set_BothQoS(NewState, NewQoS, LQS)}. + +%%----------------------------------------------------------% +%% function : validate_qos +%% Arguments: Required_qos - CosNotification::QoSProperties +%% [#'Property'{name, value}, ...] where name eq. string() +%% and value eq. any(). +%% Returns : {'EXCEPTION', CosNotification::UnsupportedQoS} +%% {ok, CosNotification::NamedPropertyRangeSeq} +%%----------------------------------------------------------- +validate_qos(_OE_THIS, _OE_FROM, State, Required_qos) -> + QoS = 'CosNotification_Common':validate_qos(Required_qos, ?get_BothQoS(State), + proxy, ?get_MyAdmin(State), + false), + {reply, {ok, QoS}, State}. + +%%----- Inherit from CosNotifyComm::NotifySubscribe --------- +%%----------------------------------------------------------% +%% function : subscription_change +%% Arguments: Added - #'CosNotification_EventType'{} +%% Removed - #'CosNotification_EventType'{} +%% Returns : ok | +%% {'EXCEPTION', #'CosNotifyComm_InvalidEventType'{}} +%%----------------------------------------------------------- +subscription_change(_OE_THIS, _OE_FROM, State, Added, Removed) -> + cosNotification_Filter:validate_types(Added), + cosNotification_Filter:validate_types(Removed), + %% On this "side", we care about which type of events the client + %% will require, since the client (or an agent) clearly stated + %% that it's only interested in these types of events. + %% Also see PusherConsumer- and PullerConsumer-'offer_change'. + update_subscribe(remove, State, Removed), + CurrentSub = ?get_AllSubscribe(State), + NewState = + case cosNotification_Filter:check_types(Added++CurrentSub) of + true -> + %% Types supplied does in some way cause all events to be valid. + %% Smart? Would have been better to not supply any at all. + ?set_SubscribeData(State, true); + {ok, Which, WC} -> + ?set_SubscribeData(State, {Which, WC}) + end, + update_subscribe(add, NewState, Added), + case ?get_SubscribeType(NewState) of + true -> + %% Perhaps we should handle exception here?! + %% Probably not. Better to stay "on-line". + catch 'CosNotifyComm_NotifyPublish': + offer_change(?get_Client(NewState), Added, Removed), + ok; + _-> + ok + end, + {reply, ok, NewState}. + +update_subscribe(_, _, [])-> + ok; +update_subscribe(add, State, [H|T]) -> + ?add_Subscribe(State, H), + update_subscribe(add, State, T); +update_subscribe(remove, State, [H|T]) -> + ?del_Subscribe(State, H), + update_subscribe(remove, State, T). + +%%----- Inherit from CosNotifyFilter::FilterAdmin ----------- +%%----------------------------------------------------------% +%% function : add_filter +%% Arguments: Filter - CosNotifyFilter::Filter +%% Returns : FilterID - long +%%----------------------------------------------------------- +add_filter(_OE_THIS, _OE_FROM, State, Filter) -> + 'CosNotification_Common':type_check(Filter, 'CosNotifyFilter_Filter'), + FilterID = ?new_Id(State), + NewState = ?set_IdCounter(State, FilterID), + {reply, FilterID, ?add_Filter(NewState, FilterID, Filter)}. + +%%----------------------------------------------------------% +%% function : remove_filter +%% Arguments: FilterID - long +%% Returns : ok +%%----------------------------------------------------------- +remove_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ok, ?del_Filter(State, FilterID)}; +remove_filter(_,_,_,What) -> + orber:dbg("[~p] PusherSupplier:remove_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_filter +%% Arguments: FilterID - long +%% Returns : Filter - CosNotifyFilter::Filter | +%% {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}} +%%----------------------------------------------------------- +get_filter(_OE_THIS, _OE_FROM, State, FilterID) when is_integer(FilterID) -> + {reply, ?get_Filter(State, FilterID), State}; +get_filter(_,_,_,What) -> + orber:dbg("[~p] PusherSupplier:get_filter(~p); Not an integer", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%----------------------------------------------------------% +%% function : get_all_filters +%% Arguments: - +%% Returns : Filter - CosNotifyFilter::FilterIDSeq +%%----------------------------------------------------------- +get_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ?get_AllFilterID(State), State}. + +%%----------------------------------------------------------% +%% function : remove_all_filters +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +remove_all_filters(_OE_THIS, _OE_FROM, State) -> + {reply, ok, ?del_AllFilter(State)}. + + +%%----- Inherit from CosEventComm::PushSupplier ------------- +%%----------------------------------------------------------% +%% function : disconnect_push_supplier +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_push_supplier(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----- Inherit from CosNotifyComm::StructuredPushSupplier -- +%%----------------------------------------------------------% +%% function : disconnect_structured_push_supplier +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_structured_push_supplier(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%----- Inherit from CosNotifyComm::SequencePushSupplier ---- +%%----------------------------------------------------------% +%% function : disconnect_sequence_push_supplier +%% Arguments: - +%% Returns : ok +%%----------------------------------------------------------- +disconnect_sequence_push_supplier(_OE_THIS, _OE_FROM, State) -> + {stop, normal, ok, ?set_Unconnected(State)}. + +%%--------------- LOCAL FUNCTIONS ---------------------------- +find_obj({value, {_, Obj}}) -> Obj; +find_obj(_) -> {'EXCEPTION', #'CosNotifyFilter_FilterNotFound'{}}. + +find_ids(List) -> find_ids(List, []). +find_ids([], Acc) -> Acc; +find_ids([{I,_}|T], Acc) -> find_ids(T, [I|Acc]); +find_ids(What, _) -> + orber:dbg("[~p] PusherSupplier:find_ids();~n" + "Id corrupt: ~p", [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}). + +%% Delete a single object. +%% The list do not differ, i.e., no filter removed, raise exception. +delete_obj(List,List) -> corba:raise(#'CosNotifyFilter_FilterNotFound'{}); +delete_obj(List,_) -> List. + +%%----------------------------------------------------------- +%% function : callSeq +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callSeq(_OE_THIS, OE_FROM, State, EventsIn, Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:validate_event(?get_SubscribeData(State), EventsIn, + ?get_AllFilter(State), + ?get_SubscribeDB(State), + Status) of + {[],_} -> + ?DBG("PROXY NOSUBSCRIPTION SEQUENCE/STRUCTURED: ~p~n",[EventsIn]), + {noreply, State}; + {Events,_} when ?is_Suspended(State) -> + store_events(State, Events), + {noreply, State}; + {Events,_} when ?is_UnConnected(State) -> + orber:dbg("[~p] PusherSupplier:callAny();~n" + "Not connected, dropping event(s): ~p", + [?LINE, Events], ?DEBUG_LEVEL), + {noreply, State}; + {[Event],_} when ?is_STRUCTURED(State) -> + ?DBG("PROXY RECEIVED SEQUENCE: ~p~n",[Event]), + empty_db(State, ?addAndGet_Event(State, Event)); + {[Event],_} when ?is_ANY(State) -> + ?DBG("PROXY RECEIVED SEQUENCE: ~p~n",[Event]), + AnyEvent = any:create('CosNotification_StructuredEvent':tc(),Event), + empty_db(State, ?addAndGet_Event(State, AnyEvent)); + {Events,_} -> + ?DBG("PROXY RECEIVED SEQUENCE: ~p~n",[Events]), + store_events(State, Events), + lookup_and_push(State) + end. + +%%----------------------------------------------------------- +%% function : callAny +%% Arguments: +%% Returns : +%%----------------------------------------------------------- +callAny(_OE_THIS, OE_FROM, State, EventIn, Status) -> + corba:reply(OE_FROM, ok), + case cosNotification_eventDB:validate_event(?get_SubscribeData(State), EventIn, + ?get_AllFilter(State), + ?get_SubscribeDB(State), + Status) of + {[],_} -> + ?DBG("PROXY NOSUBSCRIPTION ANY: ~p~n",[EventIn]), + %% To be on the safe side, test if there are any events that not + %% have been forwarded (should only be possible if StartTime is used). + lookup_and_push(State); + {Event,_} when ?is_Suspended(State), ?is_ANY(State) -> + ?add_Event(State, Event), + {noreply, State}; + {Event,_} when ?is_Suspended(State) -> + ?add_Event(State, ?not_CreateSE("","%ANY","",[],[],Event)), + {noreply, State}; + {Event,_} when ?is_UnConnected(State) -> + orber:dbg("[~p] PusherSupplier:callAny();~n" + "Not connected, dropping event: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + {noreply, State}; + {Event,_} when ?is_ANY(State) -> + ?DBG("PROXY RECEIVED ANY: ~p~n",[Event]), + %% We must store the event since there may be other events that should + %% be delivered first, e.g., higher priority. + empty_db(State, ?addAndGet_Event(State, Event)); + {Event,_} when ?is_SEQUENCE(State) -> + ?DBG("PROXY RECEIVED ANY==>SEQUENCE: ~p~n",[Event]), + StrEvent = ?not_CreateSE("","%ANY","",[],[],Event), + ?add_Event(State, StrEvent), + lookup_and_push(State); + {Event,_} -> + ?DBG("PROXY RECEIVED ANY==>STRUCTURED: ~p~n",[Event]), + StrEvent = ?not_CreateSE("","%ANY","",[],[],Event), + empty_db(State, ?addAndGet_Event(State, StrEvent)) + end. + +%% Lookup and push "the correct" amount of events. +lookup_and_push(State) -> + %% The boolean indicates, if false, that we will only push events if we have + %% passed the BatchLimit. If true we will ignore this limit and push events + %% anyway (typcially invoked when pacing limit passed). + lookup_and_push(State, false). +lookup_and_push(State, false) when ?is_SEQUENCE(State) -> + case ?is_BatchLimitReached(State) of + true -> + case ?get_Events(State, ?get_BatchLimit(State)) of + {[], _, _} -> + ?DBG("BATCHLIMIT (~p) REACHED BUT NO EVENTS FOUND~n", + [?get_BatchLimit(State)]), + {noreply, State}; + {Events, _, Keys} -> + ?DBG("BATCHLIMIT (~p) REACHED, EVENTS FOUND: ~p~n", + [?get_BatchLimit(State), Events]), + case catch 'CosNotifyComm_SequencePushConsumer': + push_structured_events(?get_Client(State), Events) of + ok -> + cosNotification_eventDB:delete_events(Keys), + lookup_and_push(reset_cache(State), false); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + ?DBG("PUSH SUPPLIER CLIENT NO LONGER EXIST~n", []), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + What when ?is_PersistentEvent(State), + ?is_PersistentConnection(State) -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p", + [?LINE, What], ?DEBUG_LEVEL), + check_cache(State); + What when ?is_PersistentConnection(State) -> + %% Here we should do something when we want to handle + %% Persistent EventReliability. + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p~n" + "Dropping events: ~p", + [?LINE, What, Events], ?DEBUG_LEVEL), + cosNotification_eventDB:delete_events(Keys), + {noreply, State}; + WhatII -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p~n" + "Terminating and dropping events: ~p", + [?LINE, WhatII, Events], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, WhatII}]), + {stop, normal, State} + end + end; + _ -> + ?DBG("BATCHLIMIT (~p) NOT REACHED~n",[?get_BatchLimit(State)]), + {noreply, State} + end; +lookup_and_push(State, true) when ?is_SEQUENCE(State) -> + case ?get_Events(State, ?get_BatchLimit(State)) of + {[], _, _} -> + ?DBG("PACELIMIT REACHED BUT NO EVENTS FOUND~n", []), + {noreply, State}; + {Events, _, Keys} -> + ?DBG("PACELIMIT REACHED, EVENTS FOUND: ~p~n", [Events]), + case catch 'CosNotifyComm_SequencePushConsumer': + push_structured_events(?get_Client(State), Events) of + ok -> + cosNotification_eventDB:delete_events(Keys), + lookup_and_push(reset_cache(State), false); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client no longer exists; terminating and dropping events: ~p", + [?LINE, Events], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + What when ?is_PersistentEvent(State), + ?is_PersistentConnection(State) -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p", + [?LINE, What], ?DEBUG_LEVEL), + check_cache(State); + What when ?is_PersistentConnection(State) -> + %% Here we should do something when we want to handle + %% Persistent EventReliability. + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p~n" + "Dropping events: ~p", + [?LINE, What, Events], ?DEBUG_LEVEL), + cosNotification_eventDB:delete_events(Keys), + {noreply, State}; + WhatII -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p~n" + "Terminating and dropping events: ~p", + [?LINE, WhatII, Events], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, WhatII}]), + {stop, normal, State} + end + end; +lookup_and_push(State, _) -> + empty_db(State, ?get_Event(State)). + + +%% Push all events stored while not connected or received in sequence. +empty_db(State, {[], _, _}) -> + {noreply, State}; +empty_db(State, {Event, _, Keys}) when ?is_STRUCTURED(State) -> + case catch 'CosNotifyComm_StructuredPushConsumer': + push_structured_event(?get_Client(State), Event) of + ok -> + cosNotification_eventDB:delete_events(Keys), + NewState = reset_cache(State), + empty_db(NewState, ?get_Event(NewState)); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + orber:dbg("[~p] PusherSupplier:empty_db();~n" + "Client no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + What when ?is_PersistentEvent(State), + ?is_PersistentConnection(State) -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p", + [?LINE, What], ?DEBUG_LEVEL), + check_cache(State); + What when ?is_PersistentConnection(State) -> + %% Here we should do something when we want to handle + %% Persistent EventReliability. + orber:dbg("[~p] PusherSupplier:empty_db();~n" + "Client respond incorrect: ~p~n" + "Dropping event: ~p", + [?LINE, What, Event], ?DEBUG_LEVEL), + cosNotification_eventDB:delete_events(Keys), + {noreply, State}; + WhatII -> + orber:dbg("[~p] PusherSupplier:empty_db();~n" + "Client respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, WhatII, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, WhatII}]), + {stop, normal, State} + end; +empty_db(State, {Event, _, Keys}) when ?is_ANY(State) -> + case catch 'CosEventComm_PushConsumer':push(?get_Client(State), Event) of + ok -> + cosNotification_eventDB:delete_events(Keys), + NewState = reset_cache(State), + empty_db(NewState, ?get_Event(NewState)); + {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') orelse + is_record(E, 'NO_PERMISSION') orelse + is_record(E, 'CosEventComm_Disconnected') -> + orber:dbg("[~p] PusherSupplier:empty_db();~n" + "Client no longer exists; terminating and dropping: ~p", + [?LINE, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, {'EXCEPTION', E}}]), + {stop, normal, State}; + What when ?is_PersistentEvent(State), + ?is_PersistentConnection(State) -> + orber:dbg("[~p] PusherSupplier:lookup_and_push();~n" + "Client respond incorrect: ~p", + [?LINE, What], ?DEBUG_LEVEL), + check_cache(State); + What when ?is_PersistentConnection(State) -> + %% Here we should do something when we want to handle + %% Persistent EventReliability. + orber:dbg("[~p] PusherSupplier:empty_db();~n" + "Client respond incorrect: ~p~n" + "Dropping Event: ~p", + [?LINE, What, Event], ?DEBUG_LEVEL), + cosNotification_eventDB:delete_events(Keys), + {noreply, State}; + WhatII -> + orber:dbg("[~p] PusherSupplier:empty_db();~n" + "Client respond incorrect: ~p~n" + "Terminating and dropping: ~p", + [?LINE, WhatII, Event], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, ?get_Client(State)}, + {reason, WhatII}]), + {stop, normal, State} + end. + +reset_cache(#state{cacheTimeout = undefined, + cacheInterval = undefined} = State) -> + State; +reset_cache(State) -> + stop_timer(State#state.cacheTimeout), + stop_timer(State#state.cacheInterval), + State#state{cacheTimeout = undefined, + cacheInterval = undefined}. + +check_cache(#state{maxCache = Max, cacheTimeout = Timeout, + cacheInterval = Interval} = State) -> + case cosNotification_eventDB:status(State#state.eventDB, eventCounter) of + Count when Count > Max -> + %% Reached the upper limit, terminate. + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, State#state.client}, + {reason, {max_events, Max}}]), + {stop, normal, State}; + _ when Timeout == undefined, Interval == undefined -> + case {timer:send_interval(cosNotificationApp:interval_events(), + cacheInterval), + timer:send_after(cosNotificationApp:timeout_events(), + cacheTimeout)} of + {{ok, IntervalRef}, {ok, TimeoutRef}} -> + {noreply, State#state{cacheTimeout = TimeoutRef, + cacheInterval = IntervalRef}}; + Error -> + orber:dbg("[~p] PusherSupplier:check_cache();~n" + "Unable to start timers: ~p", + [?LINE, Error], ?DEBUG_LEVEL), + 'CosNotification_Common':notify([{proxy, State#state.this}, + {client, State#state.client}, + {reason, {timer, Error}}]), + {stop, normal, State} + end; + _ -> + %% Timers already started. + {noreply, State} + end. + +store_events(_State, []) -> + ok; +store_events(State, [Event|Rest]) when ?is_ANY(State) -> + AnyEvent = any:create('CosNotification_StructuredEvent':tc(),Event), + ?add_Event(State, AnyEvent), + store_events(State, Rest); +store_events(State, [Event|Rest]) -> + ?add_Event(State, Event), + store_events(State, Rest). + +%% Start timers which send a message each time we should push events. Only used +%% when this objects is defined to supply sequences. +start_timer(State) -> + case ?get_PacingInterval(State) of + 0 -> + ?DBG("PUSH SUPPLIER STARTED NO TIMER (0), BATCH LIMIT: ~p~n", + [?get_BatchLimit(State)]), + + State; + PacInt -> + case catch timer:send_interval(timer:seconds(PacInt), pacing) of + {ok,PacTRef} -> + ?DBG("PUSH SUPPLIER STARTED TIMER, BATCH LIMIT: ~p~n", + [?get_BatchLimit(State)]), + ?set_PacingTimer(State, PacTRef); + What -> + orber:dbg("[~p] PusherSupplier:start_timer();~n" + "Unable to invoke timer:send_interval/2: ~p", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end + end. + +stop_timer(undefined) -> + ?DBG("PUSH SUPPLIER HAVE NO TIMER TO STOP~n",[]), + ok; +stop_timer(Timer) -> + ?DBG("PUSH SUPPLIER STOPPED TIMER~n",[]), + timer:cancel(Timer), + ok. + + +%%--------------- MISC FUNCTIONS, E.G. DEBUGGING ------------- +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/cosNotification.app.src b/lib/cosNotification/src/cosNotification.app.src new file mode 100644 index 0000000000..04beac36e8 --- /dev/null +++ b/lib/cosNotification/src/cosNotification.app.src @@ -0,0 +1,120 @@ +{application, cosNotification, + [{description, "The Erlang CosNotification application"}, + {vsn, "%VSN%"}, + {modules, + [ + 'CosNotification_Common', + 'CosNotifyChannelAdmin_ConsumerAdmin_impl', + 'CosNotifyChannelAdmin_EventChannelFactory_impl', + 'CosNotifyChannelAdmin_EventChannel_impl', + 'CosNotifyChannelAdmin_SupplierAdmin_impl', + 'PullerConsumer_impl', + 'PullerSupplier_impl', + 'PusherConsumer_impl', + 'PusherSupplier_impl', + 'cosNotificationApp', + 'CosNotifyFilter_Filter_impl', + 'CosNotifyFilter_MappingFilter_impl', + 'CosNotifyFilter_FilterFactory_impl', + 'cosNotification_Scanner', + 'cosNotification_Grammar', + 'cosNotification_Filter', + 'cosNotification_eventDB', + 'oe_CosNotification', + 'oe_cosNotificationAppComm', + 'oe_CosNotificationComm_Event', + 'CosNotification', + 'CosNotification_AdminPropertiesAdmin', + 'CosNotification_EventHeader', + 'CosNotification_EventType', + 'CosNotification_FixedEventHeader', + 'CosNotification_NamedPropertyRange', + 'CosNotification_Property', + 'CosNotification_PropertyError', + 'CosNotification_PropertyRange', + 'CosNotification_QoSAdmin', + 'CosNotification_StructuredEvent', + 'CosNotification_UnsupportedAdmin', + 'CosNotification_UnsupportedQoS', + 'CosNotification_EventBatch', + 'CosNotification_EventTypeSeq', + 'CosNotification_NamedPropertyRangeSeq', + 'CosNotification_PropertyErrorSeq', + 'CosNotification_PropertySeq', + 'oe_CosNotifyChannelAdmin', + 'CosNotifyChannelAdmin_AdminLimit', + 'CosNotifyChannelAdmin_AdminLimitExceeded', + 'CosNotifyChannelAdmin_AdminNotFound', + 'CosNotifyChannelAdmin_ChannelNotFound', + 'CosNotifyChannelAdmin_ConnectionAlreadyActive', + 'CosNotifyChannelAdmin_ConnectionAlreadyInactive', + 'CosNotifyChannelAdmin_ConsumerAdmin', + 'CosNotifyChannelAdmin_EventChannel', + 'CosNotifyChannelAdmin_EventChannelFactory', + 'CosNotifyChannelAdmin_NotConnected', + 'CosNotifyChannelAdmin_ProxyConsumer', + 'CosNotifyChannelAdmin_ProxyNotFound', + 'CosNotifyChannelAdmin_ProxyPullConsumer', + 'CosNotifyChannelAdmin_ProxyPullSupplier', + 'CosNotifyChannelAdmin_ProxyPushConsumer', + 'CosNotifyChannelAdmin_ProxyPushSupplier', + 'CosNotifyChannelAdmin_ProxySupplier', + 'CosNotifyChannelAdmin_SequenceProxyPullConsumer', + 'CosNotifyChannelAdmin_SequenceProxyPullSupplier', + 'CosNotifyChannelAdmin_SequenceProxyPushConsumer', + 'CosNotifyChannelAdmin_SequenceProxyPushSupplier', + 'CosNotifyChannelAdmin_StructuredProxyPullConsumer', + 'CosNotifyChannelAdmin_StructuredProxyPullSupplier', + 'CosNotifyChannelAdmin_StructuredProxyPushConsumer', + 'CosNotifyChannelAdmin_StructuredProxyPushSupplier', + 'CosNotifyChannelAdmin_SupplierAdmin', + 'CosNotifyChannelAdmin_AdminIDSeq', + 'CosNotifyChannelAdmin_ChannelIDSeq', + 'CosNotifyChannelAdmin_ProxyIDSeq', + 'oe_CosNotifyComm', + 'CosNotifyComm_InvalidEventType', + 'CosNotifyComm_NotifyPublish', + 'CosNotifyComm_NotifySubscribe', + 'CosNotifyComm_PullConsumer', + 'CosNotifyComm_PullSupplier', + 'CosNotifyComm_PushConsumer', + 'CosNotifyComm_PushSupplier', + 'CosNotifyComm_SequencePullConsumer', + 'CosNotifyComm_SequencePullSupplier', + 'CosNotifyComm_SequencePushConsumer', + 'CosNotifyComm_SequencePushSupplier', + 'CosNotifyComm_StructuredPullConsumer', + 'CosNotifyComm_StructuredPullSupplier', + 'CosNotifyComm_StructuredPushConsumer', + 'CosNotifyComm_StructuredPushSupplier', + 'oe_CosNotifyFilter', + 'CosNotifyFilter_CallbackNotFound', + 'CosNotifyFilter_ConstraintExp', + 'CosNotifyFilter_ConstraintInfo', + 'CosNotifyFilter_ConstraintNotFound', + 'CosNotifyFilter_DuplicateConstraintID', + 'CosNotifyFilter_Filter', + 'CosNotifyFilter_FilterAdmin', + 'CosNotifyFilter_FilterFactory', + 'CosNotifyFilter_FilterNotFound', + 'CosNotifyFilter_InvalidConstraint', + 'CosNotifyFilter_InvalidGrammar', + 'CosNotifyFilter_InvalidValue', + 'CosNotifyFilter_MappingConstraintInfo', + 'CosNotifyFilter_MappingConstraintPair', + 'CosNotifyFilter_MappingFilter', + 'CosNotifyFilter_UnsupportedFilterableData', + 'CosNotifyFilter_CallbackIDSeq', + 'CosNotifyFilter_ConstraintExpSeq', + 'CosNotifyFilter_ConstraintIDSeq', + 'CosNotifyFilter_ConstraintInfoSeq', + 'CosNotifyFilter_FilterIDSeq', + 'CosNotifyFilter_MappingConstraintInfoSeq', + 'CosNotifyFilter_MappingConstraintPairSeq' + ] + }, + {registered, [cosNotificationSup, oe_cosNotificationFactory]}, + {applications, [orber, stdlib, kernel]}, + {env, []}, + {mod, {cosNotificationApp, []}} +]}. diff --git a/lib/cosNotification/src/cosNotification.appup.src b/lib/cosNotification/src/cosNotification.appup.src new file mode 100644 index 0000000000..6c3b2833b7 --- /dev/null +++ b/lib/cosNotification/src/cosNotification.appup.src @@ -0,0 +1,7 @@ +{"%VSN%", + [ + ], + [ + ] +}. + diff --git a/lib/cosNotification/src/cosNotificationApp.erl b/lib/cosNotification/src/cosNotificationApp.erl new file mode 100644 index 0000000000..ba44163272 --- /dev/null +++ b/lib/cosNotification/src/cosNotificationApp.erl @@ -0,0 +1,447 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : cosNotificationApp.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module(cosNotificationApp). + +%%--------------- INCLUDES ----------------------------------- +%% Local +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). +%%--------------- EXPORTS------------------------------------- +%% cosNotification API external +-export([start/0, stop/0, + start_factory/1, start_factory/0, stop_factory/1, + start_global_factory/0, start_global_factory/1, + start_filter_factory/1, start_filter_factory/0, stop_filter_factory/1, + install/0, install/1, uninstall/0, uninstall/1, + install_event/0, install_event/1, uninstall_event/0, uninstall_event/1, + install_typed/0, install_typed/1, uninstall_typed/0, uninstall_typed/1, + create_structured_event/6, type_check/0, notify/0, max_events/0, + timeout_events/0, interval_events/0]). + +%% Application callbacks +-export([start/2, init/1, stop/1]). + +%%--------------- DEFINES ------------------------------------ +-define(IDL_MODULES, ['oe_CosNotification', + 'oe_cosNotificationAppComm', + 'oe_CosNotifyComm', + 'oe_CosNotifyFilter', + 'oe_CosNotifyChannelAdmin']). +-define(EVENT_IDL_MODULES, ['oe_CosEventComm', + 'oe_CosEventChannelAdmin']). +-define(TYPED_IDL_MODULES, ['oe_CosTypedEvent', + 'oe_CosTypedNotification']). + +-define(FACTORY_NAME, oe_cosNotificationFactory). +-define(SUPERVISOR_NAME, cosNotificationSup). + + +%%------------------------------------------------------------ +%% function : install/X +%% Arguments: - | Time (seconds) +%% Returns : ok | EXIT | EXCEPTION +%% Effect : Install necessary data in the IFR DB +%%------------------------------------------------------------ + +install() -> + install(0). + +install(Time) when is_integer(Time) -> + install_loop(?IDL_MODULES, timer:seconds(Time)); +install(_Time) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%------------------------------------------------------------ +%% function : install_event/X +%% Arguments: - | Time (seconds) +%% Returns : ok | EXIT | EXCEPTION +%% Effect : Install necessary data in the IFR DB +%%------------------------------------------------------------ + +install_event() -> + install_event(0). + +install_event(Time) when is_integer(Time) -> + install_loop(?EVENT_IDL_MODULES, timer:seconds(Time)); +install_event(_Time) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%------------------------------------------------------------ +%% function : install_typed/X +%% Arguments: - | Time (seconds) +%% Returns : ok | EXIT | EXCEPTION +%% Effect : Install necessary data in the IFR DB +%%------------------------------------------------------------ + +install_typed() -> + install_typed(0). + +install_typed(Time) when is_integer(Time) -> + install_loop(?TYPED_IDL_MODULES, timer:seconds(Time)); +install_typed(_Time) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +install_loop([], _) -> + ok; +install_loop([H|T], Time) -> + H:'oe_register'(), + timer:sleep(Time), + install_loop(T, Time). + +%%------------------------------------------------------------ +%% function : uninstall/X +%% Arguments: - | Time (seconds) +%% Returns : ok | EXIT | EXCEPTION +%% Effect : Remove data related to cosNotificationin from the IFR DB +%%------------------------------------------------------------ + +uninstall() -> + uninstall(0). + +uninstall(Time) when is_integer(Time) -> + uninstall_loop(lists:reverse(?IDL_MODULES), timer:seconds(Time)); +uninstall(_Time) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%------------------------------------------------------------ +%% function : uninstall_event/X +%% Arguments: - | Time (seconds) +%% Returns : ok | EXIT | EXCEPTION +%% Effect : Remove data related to cosNotificationin from the IFR DB +%%------------------------------------------------------------ + +uninstall_event() -> + uninstall_event(0). + +uninstall_event(Time) when is_integer(Time) -> + uninstall_loop(lists:reverse(?EVENT_IDL_MODULES), timer:seconds(Time)); +uninstall_event(_Time) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%------------------------------------------------------------ +%% function : uninstall_typed/X +%% Arguments: - | Time (seconds) +%% Returns : ok | EXIT | EXCEPTION +%% Effect : Remove data related to cosNotificationin from the IFR DB +%%------------------------------------------------------------ + +uninstall_typed() -> + uninstall_typed(0). + +uninstall_typed(Time) when is_integer(Time) -> + uninstall_loop(lists:reverse(?TYPED_IDL_MODULES), timer:seconds(Time)); +uninstall_typed(_Time) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +uninstall_loop([], _) -> + ok; +uninstall_loop([H|T], Time) -> + H:'oe_unregister'(), + timer:sleep(Time), + uninstall_loop(T, Time). + + +%%------------------------------------------------------------ +%% function : start/stop +%% Arguments: +%% Returns : +%% Effect : Starts or stops the cosTRansaction application. +%%------------------------------------------------------------ + +start() -> + application:start(cosNotification). +stop() -> + application:stop(cosNotification). + +%%------------------------------------------------------------ +%% function : start_factory +%% Arguments: none or an argumentlist whith default values. +%% Returns : ObjectRef | {'EXCEPTION', _} | {'EXIT', Reason} +%% Effect : Starts a CosNotifyChannelAdmin_EventChannelFactory +%%------------------------------------------------------------ +start_factory() -> + start_factory(?not_DEFAULT_SETTINGS). + +start_factory(Args) when is_list(Args) -> + SO = 'CosNotification_Common':get_option(server_options, Args, ?not_DEFAULT_SETTINGS), + SPEC = ['CosNotifyChannelAdmin_EventChannelFactory',Args, + [{sup_child, true}, + {regname, {local, oe_cosNotificationFactory}}|SO]], + case supervisor:start_child(?SUPERVISOR_NAME, SPEC) of + {ok, Pid, Obj} when is_pid(Pid) -> + Obj; + Other-> + orber:dbg("[~p] cosNotificationApp:start_factory( ~p ).~n" + "Reason: ~p~n", [?LINE, Args, Other], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; +start_factory(Args) -> + orber:dbg("[~p] cosNotificationApp:start_factory( ~p ).~n" + "Bad parameters~n", [?LINE, Args], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +%%------------------------------------------------------------ +%% function : start_global_factory +%% Arguments: none or an argumentlist whith default values. +%% Returns : ObjectRef | {'EXCEPTION', _} | {'EXIT', Reason} +%% Effect : Starts a CosNotifyChannelAdmin_EventChannelFactory +%%------------------------------------------------------------ +start_global_factory() -> + start_global_factory(?not_DEFAULT_SETTINGS). + +start_global_factory(Args) when is_list(Args) -> + SO = 'CosNotification_Common':get_option(server_options, Args, ?not_DEFAULT_SETTINGS), + Name = create_name(), + SPEC = ['CosNotifyChannelAdmin_EventChannelFactory',Args, + [{sup_child, true}, + {regname, {global, Name}}|SO]], + case supervisor:start_child(?SUPERVISOR_NAME, SPEC) of + {ok, Pid, Obj} when is_pid(Pid) -> + Obj; + Other-> + orber:dbg("[~p] cosNotificationApp:start_global_factory( ~p ).~n" + "Reason: ~p~n", [?LINE, Args, Other], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; +start_global_factory(Args) -> + orber:dbg("[~p] cosNotificationApp:start_global_factory( ~p ).~n" + "Bad parameters~n", [?LINE, Args], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + + +%%------------------------------------------------------------ +%% function : stop_factory +%% Arguments: Factory Object Reference +%% Returns : ok | {'EXCEPTION', _} +%% Effect : +%%------------------------------------------------------------ + +stop_factory(Fac)-> + corba:dispose(Fac). + +%%------------------------------------------------------------ +%% function : start_filter_factory +%% Arguments: none or an argumentlist which by default is defined +%% in CosNotification_Definitions.hrl, i.e., '?not_FILTERFAC_DEF' +%% Returns : ObjectRef | {'EXCEPTION', _} | {'EXIT', Reason} +%% Effect : Starts a CosNotifyChannelAdmin_EventChannelFactory +%%------------------------------------------------------------ +start_filter_factory() -> + start_filter_factory([{typecheck, true}, + {tty, false}, + {logfile, false}, + {server_options, []}]). +start_filter_factory(Args) when is_list(Args) -> + SO = 'CosNotification_Common':get_option(server_options, Args, + ?not_DEFAULT_SETTINGS), + SPEC = ['CosNotifyFilter_FilterFactory',Args, [{sup_child, true}|SO]], + case supervisor:start_child(?SUPERVISOR_NAME, SPEC) of + {ok, Pid, Obj} when is_pid(Pid) -> + Obj; + Other-> + orber:dbg("[~p] cosNotificationApp:start_filter_factory( ~p ).~n" + "Reason: ~p~n", [?LINE, Args, Other], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; +start_filter_factory(Args) -> + orber:dbg("[~p] cosNotificationApp:start_filter_factory( ~p ).~n" + "Bad parameters~n", [?LINE, Args], ?DEBUG_LEVEL), + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + + +%%------------------------------------------------------------ +%% function : stop_filter_factory +%% Arguments: FilterFactory Object Reference +%% Returns : ok | {'EXCEPTION', _} +%% Effect : +%%------------------------------------------------------------ + +stop_filter_factory(Fac)-> + corba:dispose(Fac). + + +%%------------------------------------------------------------ +%% function : create_structured_event +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ +create_structured_event(StrD,StrT,StrE,PSeqV,PSeqF,AnyR) + when is_list(StrD) andalso is_list(StrT) andalso is_list(StrE) + andalso is_list(PSeqV) andalso is_list(PSeqF) andalso + is_record(AnyR, any) -> +#'CosNotification_StructuredEvent'{header = + #'CosNotification_EventHeader'{fixed_header = + #'CosNotification_FixedEventHeader'{event_type = + #'CosNotification_EventType'{domain_name=StrD, + type_name=StrT}, + event_name = StrE}, + variable_header = PSeqV}, + filterable_data = PSeqF, + remainder_of_body = AnyR}; +create_structured_event(_StrD,_StrT,_StrE,_PSeqV,_PSeqF,_AnyR) -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + + +%%------------------------------------------------------------ +%% function : type_check +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ +type_check() -> + case application:get_env(cosNotification, type_check) of + {ok, Boolean} when is_atom(Boolean) -> + Boolean; + _ -> + true + end. + +%%------------------------------------------------------------ +%% function : notify +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ +notify() -> + case application:get_env(cosNotification, notify) of + {ok, Module} when is_atom(Module) -> + Module; + _ -> + false + end. + +%%------------------------------------------------------------ +%% function : max_events +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ +max_events() -> + case application:get_env(cosNotification, max_events) of + {ok, Max} when is_integer(Max) -> + Max; + _ -> + 50 + end. + +%%------------------------------------------------------------ +%% function : timeout_events +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ +timeout_events() -> + case application:get_env(cosNotification, timeout_events) of + {ok, Max} when is_integer(Max) -> + Max; + _ -> + 3000000 %% 5 minutes + end. + + +%%------------------------------------------------------------ +%% function : interval_events +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ +interval_events() -> + case application:get_env(cosNotification, interval_events) of + {ok, Max} when is_integer(Max) -> + Max; + _ -> + 10000 %% 10 seconds + end. + + +%%------------------------------------------------------------ +%% function : start +%% Arguments: Type - see module application +%% Arg - see module application +%% Returns : +%% Effect : Module callback for application +%%------------------------------------------------------------ + +start(_, _) -> + supervisor:start_link({local, ?SUPERVISOR_NAME}, cosNotificationApp, app_init). + + +%%------------------------------------------------------------ +%% function : stop +%% Arguments: Arg - see module application +%% Returns : +%% Effect : Module callback for application +%%------------------------------------------------------------ + +stop(_) -> + ok. + +%%------------------------------------------------------------ +%% function : init +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ + +%% Starting using create_factory/X +init(own_init) -> + {ok,{{simple_one_for_one,50,10}, + [{"oe_NotChild", + {'CosNotification_Common',create_link, []}, + transient,100000,worker, + ['CosNotifyChannelAdmin_EventChannel', + 'CosNotifyChannelAdmin_EventChannel_impl']}]}}; +%% When starting as an application. +init(app_init) -> + {ok,{{simple_one_for_one,50,10}, + [{"oe_NotChild", + {'CosNotification_Common',create_link, []}, + transient,100000,worker, + ['CosNotifyChannelAdmin_EventChannel', + 'CosNotifyChannelAdmin_EventChannel_impl']}]}}. + + + +%%------------------------------------------------------------ +%% function : create_name +%% Arguments: +%% Returns : +%% Effect : Create a unique name to use when, for eaxmple, starting +%% a new server. +%%------------------------------------------------------------ +create_name() -> + {MSec, Sec, USec} = erlang:now(), + lists:concat(['oe_',node(),'_',MSec, '_', Sec, '_', USec]). + +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/cosNotificationAppComm.idl b/lib/cosNotification/src/cosNotificationAppComm.idl new file mode 100644 index 0000000000..09e0af2568 --- /dev/null +++ b/lib/cosNotification/src/cosNotificationAppComm.idl @@ -0,0 +1,17 @@ +#ifndef _OE_COSNOTIFICATIONCOMM_IDL_ +#define _OE_COSNOTIFICATIONCOMM_IDL_ + +#include<CosNotification.idl> + +module oe_CosNotificationComm { + + interface Event { + + enum status {MATCH, MATCHED}; + void callSeq(in CosNotification::EventBatch events, in status stat); + void callAny(in any event, in status stat); + }; + +}; +#endif /* ifndef _OE_COSNOTIFICATIONCOMM_IDL_ */ + diff --git a/lib/cosNotification/src/cosNotificationComm.cfg b/lib/cosNotification/src/cosNotificationComm.cfg new file mode 100644 index 0000000000..89d207528d --- /dev/null +++ b/lib/cosNotification/src/cosNotificationComm.cfg @@ -0,0 +1,3 @@ +{this, "oe_CosNotificationComm::Event"}. +{from, "oe_CosNotificationComm::Event"}. +{{handle_info, "oe_CosNotificationComm::Event"}, true}. diff --git a/lib/cosNotification/src/cosNotification_Filter.erl b/lib/cosNotification/src/cosNotification_Filter.erl new file mode 100644 index 0000000000..dd3b5beb93 --- /dev/null +++ b/lib/cosNotification/src/cosNotification_Filter.erl @@ -0,0 +1,964 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : cosNotification_Filter.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module(cosNotification_Filter). + + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%% Internal Filter Functions +-export([eval/1, + eval/2, + create_filter/1, + check_types/1, + match_types/3, + validate_types/1]). + +%%--------------- DEFINES ------------------------------------ +-define(EVENT_PATH, [{dotid,"header"}, {dotid,"fixed_header"}, + {dotid,"event_name"}]). +-define(DOMAIN_PATH, [{dotid,"header"}, {dotid,"fixed_header"}, + {dotid,"event_type"}, {dotid,"domain_name"}]). +-define(TYPE_PATH, [{dotid,"header"}, {dotid,"fixed_header"}, + {dotid,"event_type"}, {dotid,"type_name"}]). +-define(VARIABLE_PATH(I), [{dotid,"header"}, {dotid,"variable_header"}, {dotid,I}]). +-define(FILTERABLE_PATH(I), [{dotid,"filterable_data"}, {dotid,I}]). + + +%%------------------------------------------------------------ +%%--------------- FILTER FUNCTIONS --------------------------- +%%------------------------------------------------------------ +%%------------------------------------------------------------ +%% function : create_filter/1 +%% Arguments: String - Filter grammar +%% Returns : +%% Effect : +%%------------------------------------------------------------ +create_filter(Str) -> + {ok, Tokens} = cosNotification_Scanner:scan(Str), + case cosNotification_Grammar:parse(Tokens) of + {ok, Filter} -> + {ok, Filter}; + _-> + corba:raise(#'CosNotifyFilter_InvalidConstraint'{constr = Str}) + end. + +%%------------------------------------------------------------ +%% function : eval +%% Arguments: +%% Returns : +%% Effect : +%%------------------------------------------------------------ + +eval('$empty') -> true; +eval(Tree) -> eval(Tree, []). + + +%% Leaf expressions (literals and idents). +eval('$empty', _) -> true; +eval(Lit, _Env) when is_number(Lit) -> Lit; +eval(Lit, _Env) when is_list(Lit) -> Lit; %list == string +eval(Lit, _Env) when is_atom(Lit) -> Lit; %atom == bool +eval({component, V}, []) -> + %% Cannot evaluate variables at this stage. + throw({error, {unbound_variable, V}}); +eval({component, V}, Env) -> + case catch lookup(V, Env, undefined) of + {ok, Val} -> + Val; + _X -> + {error, {unbound_variable, V}} + end; + +%% CORBA2.3-15/26 states: +%% "The name parameters in tk_objref, tk_struct, tk_union, tk_enum, tk_alias, +%% tk_value, tk_value_box, tk_abstract_interface, tk_native and tk_except TypeCodes +%% and the member name parameters in tk_struct, tk_union, tk_enum, tk_value and +%% tk_except TypeCodes are not specified by (or significant in) GIOP. Agents should +%% not make assumptions about type equivalence based on these name values; only the +%% structural information (including RepositoryId values, if provided) is +%% significant. If provided, the strings should be the simple, unscoped names +%% supplied in the OMG IDL definition text. If omitted, they are encoded as empty +%% strings." +%% Makes it rather hard to follow the grammar 100 %. +eval({default_component, V}, Env) -> + case catch lookup(V, Env, default_component) of + {ok, false} -> + false; + {ok, true} -> + true; + _X -> + {error, {unbound_variable, V}} + end; +eval({exist_component, V}, Env) -> + case catch lookup(V, Env, exist_component) of + {ok, false} -> + false; + {ok, _} -> + true; + {error, _} -> + false; + _X -> + {error, {unbound_variable, V}} + end; +%% Arithmetic expressions. +eval({'*', X, Y}, Env) -> + eval_arith({fun(_X, _Y) -> _X*_Y end, X, Y}, Env); +eval({'/', X, Y}, Env) -> + eval_arith({fun(_X, _Y) -> _X/_Y end, X, Y}, Env); +eval({'+', X, Y}, Env) -> + eval_arith({fun(_X, _Y) -> _X+_Y end, X, Y}, Env); +eval({'-', X, Y}, Env) -> + eval_arith({fun(_X, _Y) -> _X-_Y end, X, Y}, Env); +eval({'u-', X}, Env) -> + eval_arith({fun(_X) -> -_X end, X}, Env); +%% Relational expressions. +eval({'==', X, Y}, Env) -> + eval_rel({fun(_X, _Y) -> _X == _Y end, X, Y}, Env); +eval({'!=', X, Y}, Env) -> + eval_rel({fun(_X, _Y) -> _X /= _Y end, X, Y}, Env); +eval({'<', X, Y}, Env) -> + eval_rel({fun(_X, _Y) -> _X < _Y end, X, Y}, Env); +eval({'<=', X, Y}, Env) -> + eval_rel({fun(_X, _Y) -> _X =< _Y end, X, Y}, Env); +eval({'>', X, Y}, Env) -> + eval_rel({fun(_X, _Y) -> _X > _Y end, X, Y}, Env); +eval({'>=', X, Y}, Env) -> + eval_rel({fun(_X, _Y) -> _X >= _Y end, X, Y}, Env); +eval({'~', Needle, Haystack}, Env) -> %substring match + N = eval(Needle, Env), + H = eval(Haystack, Env), + if + is_list(N) andalso is_list(H) -> + string:str(H, N) /= 0; + true -> + throw({error, {bad_type, Needle, Haystack}}) + end; +eval({'in', Needle, Haystack}, Env) -> %set membership + N = eval(Needle, Env), + H = eval(Haystack, Env), + if + is_list(H) -> + lists:member(N, H); + true -> + throw({error, {bad_type, Needle, Haystack}}) + end; +%% Boolean expressions. +eval({'and', false, _Y}, _Env) -> + false; +eval({'and', _X, false}, _Env) -> + false; +eval({'and', X, Y}, Env) -> + eval_and_bool({fun(_X, _Y) -> _X and _Y end, X, Y}, Env); + +eval({'or', true, _Y}, _Env) -> + true; +eval({'or', _X, true}, _Env) -> + true; +eval({'or', X, Y}, Env) -> + eval_or_bool({fun(_X, _Y) -> _X or _Y end, X, Y}, Env); +eval({'not', X}, Env) -> + eval_bool({fun(_X) -> not _X end, X}, Env); +%% Catch-all +eval(_T, _Env) -> + throw({error, internal}). + +eval_bool({Fun, X}, Env) -> + Xe = eval(X, Env), + if + is_atom(Xe) -> + Fun(Xe); + true -> + throw({error, {bad_type, X}}) + end. +eval_and_bool({Fun, X, Y}, Env) -> + case eval(X, Env) of + false -> + %% No need for evaluating the other expression. + false; + Xe -> + Ye = eval(Y, Env), + if + is_atom(Xe) andalso is_atom(Ye) -> + Fun(Xe, Ye); + true -> + throw({error, {bad_type, X, Y}}) + end + end. +eval_or_bool({Fun, X, Y}, Env) -> + case eval(X, Env) of + true -> + %% No need for evaluating the other expression. + true; + Xe -> + Ye = eval(Y, Env), + if + is_atom(Xe) andalso is_atom(Ye) -> + Fun(Xe, Ye); + true -> + throw({error, {bad_type, X, Y}}) + end + end. + + +%% According to issue 2203, OMG stated that arithmetic operations involving booleans +%% is allowed. TRUE equals 1 and FALSE 0. They refer to: + +%% "We at NEC like this feature, and feel it is both required and +%% standard with the way CORBA treats boolean values. We feel it's +%% required because it allows the constraint grammar to handle +%% expressions that combine the results of boolean comparisons, +%% which we feel is typically expected of a constraint grammar +%% (e.g., ($.fruit == apples) + ($.color == red) + ($.kind == macintosh) > 2) +%% Furthermore, while we have no fundamental opposition to explicitly +%% stating that TRUE=1 and FALSE=0, we don't necessarily feel it's +%% necessary because section 12.3.1 of CORBA alread states that +%% "Boolean values are encoded as single octets, where TRUE is the +%% value 1, and FALSE is 0." Essentially, we feel CORBA already +%% defines TRUE to be 1 and FALSE to be 0, however we are not +%% opposed to adding such a statement into Notification if folks +%% feel it's necessary." +%% If still valid, see: ftp://ftp.omg.org/pub/docs/telecom/99-07-06.txt + +%% The section they refer to (CORBA-2.0) merely states that TRUE and FALSE are +%% encoded as 1 and 0 in GIOP. Does not imply that booleans may be used as numeric. +%% But, they have stated that this should be the case so..... + +remap_bool(Num) when is_number(Num) -> Num; +remap_bool(true) -> 1; +remap_bool(false) -> 0; +remap_bool(X) -> throw({error, {bad_type, X}}). + +eval_arith({Fun, X}, Env) -> + Xe = remap_bool(eval(X, Env)), + Fun(Xe); +eval_arith({Fun, X, Y}, Env) -> + Xe = remap_bool(eval(X, Env)), + Ye = remap_bool(eval(Y, Env)), + Fun(Xe, Ye). + +eval_rel({Fun, X, Y}, Env) -> + Xe = eval(X, Env), + Ye = eval(Y, Env), + if + is_number(Xe) andalso is_number(Ye) -> + Fun(Xe, Ye); + is_list(Xe) andalso is_list(Ye) -> + Fun(Xe, Ye); + is_atom(Xe) andalso is_atom(Ye) -> + Fun(Xe, Ye); + true -> + throw({error, {bad_type, X, Y}}) + end. + +%%------------------------------------------------------------ +%% function : get_variable +%% Arguments: A sequence of CosNotification::Property{}, i.e., +%% name-value pairs. +%% ID - name in the Property +%% Any - remainder of body +%% Returns : Value in the Property | false +%% Comment : When searching for a variable we must start with +%% 'variable_header' followed by 'filterable_body'. +%% If not found we will then look in the 'remainder_of_body' +%%------------------------------------------------------------ + +get_variable([], ID, Any) when is_record(Any, any) -> + case {any:get_value(Any), any:get_typecode(Any)} of + {#'CosNotification_Property'{name=ID, value=A}, _} -> + any:get_value(A); + {_, TC} when is_atom(TC) -> + %% Since TC atom it must be a simple type, which don't have members. + throw({error, {bad_id, ID}}); + {Value, {tk_alias,_,ID,_}} when is_record(Value, any) -> + %% {tk_alias, IFRId, ID, TypeCode} + any:get_value(Value); + {Value, {tk_alias,_,ID,_}} -> + %% {tk_alias, IFRId, ID, TypeCode} + Value; + {Value, _TC} -> + get_variable([],ID, Value) + end; +get_variable([], ID, #'CosNotification_Property'{name=ID, value=Any}) -> + any:get_value(Any); +get_variable([], ID, [#'CosNotification_Property'{name=ID, value=Any}|_]) -> + any:get_value(Any); +get_variable([], ID, [H|T]) when is_record(H, 'CosNotification_Property') -> + get_variable([], ID, T); +get_variable([], ID, false) -> + throw({error, {bad_id, ID}}); +get_variable([], ID, Value) -> + M = element(1, Value), + case catch M:tc() of + {tk_struct,_,_,SList} -> + %% {tk_struct, Id, Name, ElementList}. + Field = get_field(ID, SList), + element(Field, Value); + {tk_union,_,_,_, DefNo, UList} -> + %% {tk_union, Id, Name, DiscrTC, Default, ElementList} + case id2switch(UList, ID) of + [default] when DefNo >= 0 -> + element(3, Value); + [default] -> + throw({error, {bad_id, "Bad Union ID supplied"}}); + Found -> + case catch lists:member(element(2, Value), Found) of + true -> + element(3, Value); + _ -> + throw({error, {bad_id, "Bad Union ID supplied"}}) + end + end; + _-> + throw({error, {bad_id, ID}}) + end; +get_variable([#'CosNotification_Property'{name=ID, value=A}|_], ID, _) -> + any:get_value(A); +get_variable([_|T], ID, Any) -> + get_variable(T, ID, Any). + +%%------------------------------------------------------------ +%% function : lookup +%% Arguments: T - A parse tree representing the grammar. +%% S - The event we want to extract data from +%% Op - which type of lookup should be done on this +%% component, e.g., 'default' or 'exist'. +%% Returns : {ok, boolean()} | +%% {error, _} +%% Comment : WARNING!!!! +%% This function uses some Orber core information to +%% extract data, e.g., TypeCode representation. Why? +%% We don't want to see the performance take a plunge +%% due to that users write constraints which they +%% can/may not know is slow. The alternative would be +%% to use the IFR. However, these shortcuts aren't +%% that frequent and we can easily update the code. +%% To update, investigate: +%% * lookup/3 cases related to '_type_id' +%% * lookup/3 cases related to unions +%% * get_variable/3 +%% * id2switch/2 +%% * switch2alias/2 +%%------------------------------------------------------------ +%% Done parsing, return the result. +lookup([],S,_) -> {ok, S}; +lookup('$empty', #'CosNotification_StructuredEvent'{remainder_of_body = Any},_) -> + {ok, any:get_value(Any)}; +lookup('$empty',S,_) when is_record(S, any) -> + {ok, any:get_value(S)}; + +%%------- varid -------- +%% CosNotification-revision-98-11-01/46 states: +%% "The following rules govern translation of a run-time variable, $variable , +%% into a specific event field. If the run-time variable is reserved +%% (e.g., $curtime) this translation takes precedence. Next, the first matching +%% translation is chosen respectively from: +%% * a simple-typed member of $.header.fixed_header, +%% * properties in $.header.variable_header, +%% and properties in $.header.filterable_data. +%% If no match is found, the translation defaults to $.variable. +%% Given these rules, an unstructured event with a $.priority member and a +%% structured event using $.header.variable_header(priority) can be specified +%% in a generic constraint using the run-time variable $priority . +%% Alternatively, a constraint can be written specifically for a structured or +%% unstructured event by avoiding the use of run-time variables." + +%% The above contains one error; $.header.filterable_data is not a part of the +%% header, but contained in the event body. + +%% For any events we must first verify that a path exist, e.g., +%% "header"->"fixed_header"->"event_type"->"domain_name", otherwise we will +%% use {dotid, "xxx"}. +lookup([{varid, "type_name"}|T], + #'CosNotification_StructuredEvent' + {header = #'CosNotification_EventHeader' + {fixed_header = #'CosNotification_FixedEventHeader' + {event_type = #'CosNotification_EventType'{type_name=TN}}}}, Op) -> + lookup(T, TN, Op); +lookup([{varid, "type_name"}|T], Any, Op) when is_record(Any, any) -> + case locate_var([?TYPE_PATH, ?VARIABLE_PATH("type_name"), + ?FILTERABLE_PATH("type_name")], Any, Op) of + {ok, Val} -> + lookup(T, Val, Op); + _ -> + lookup(T, get_variable([], "type_name", Any), Op) + end; +lookup([{varid, "domain_name"}|T], + #'CosNotification_StructuredEvent' + {header = #'CosNotification_EventHeader' + {fixed_header = #'CosNotification_FixedEventHeader' + {event_type = #'CosNotification_EventType'{domain_name=DN}}}}, Op) -> + lookup(T, DN, Op); +lookup([{varid, "domain_name"}|T], Any, Op) when is_record(Any, any) -> + case locate_var([?DOMAIN_PATH, ?VARIABLE_PATH("domain_name"), + ?FILTERABLE_PATH("domain_name")], Any, Op) of + {ok, Val} -> + lookup(T, Val, Op); + _ -> + lookup(T, get_variable([], "domain_name", Any), Op) + end; +lookup([{varid, "event_name"}|T], + #'CosNotification_StructuredEvent' + {header = #'CosNotification_EventHeader' + {fixed_header = #'CosNotification_FixedEventHeader' + {event_name = EN}}}, Op) -> + lookup(T, EN, Op); +lookup([{varid, "event_name"}|T], Any, Op) when is_record(Any, any) -> + case locate_var([?EVENT_PATH, ?VARIABLE_PATH("event_name"), + ?FILTERABLE_PATH("event_name")], Any, Op) of + {ok, Val} -> + lookup(T, Val, Op); + _ -> + lookup(T, get_variable([], "event_name", Any), Op) + end; + +lookup([{varid, ID}|T], + #'CosNotification_StructuredEvent'{header = + #'CosNotification_EventHeader'{variable_header = VS}, + filterable_data = FS, + remainder_of_body = Any}, Op) -> + lookup(T, get_variable(VS++FS, ID, Any), Op); +lookup([{varid, ID}|T], Any, Op) -> + case locate_var([?VARIABLE_PATH(ID), ?FILTERABLE_PATH(ID)], Any, Op) of + {ok, Val} -> + lookup(T, Val, Op); + _ -> + lookup(T, get_variable([], ID, Any), Op) + end; + +%%------- dotid -------- +%% First level +lookup([{dotid, "header"}|T], + #'CosNotification_StructuredEvent'{header = S}, Op) -> + lookup(T, S, Op); +lookup([{dotid, "filterable_data"}|T], + #'CosNotification_StructuredEvent'{filterable_data = S}, Op) -> + lookup(T, S, Op); + +lookup([{dotid, remainder_of_body}|T], + #'CosNotification_StructuredEvent'{remainder_of_body = S}, Op) -> + lookup(T, S, Op); +%% Second level. Previous token must have been header +lookup([{dotid, "fixed_header"}|T], + #'CosNotification_EventHeader'{fixed_header = S}, Op) -> + lookup(T, S, Op); +lookup([{dotid, "variable_header"}|T], + #'CosNotification_EventHeader'{variable_header = S}, Op) -> + lookup(T, S, Op); +%% Third level. Previous token must have been fixed_header. +lookup([{dotid, "event_type"}|T], + #'CosNotification_FixedEventHeader'{event_type = S}, Op) -> + lookup(T, S, Op); +lookup([{dotid, "event_name"}|T], + #'CosNotification_FixedEventHeader'{event_name = S}, Op) -> + lookup(T, S, Op); +%% Fourth level. Previous token must have been event_type +lookup([{dotid, "domain_name"}|T], #'CosNotification_EventType'{domain_name = S}, Op) -> + lookup(T, S, Op); +lookup([{dotid, "type_name"}|T], #'CosNotification_EventType'{type_name = S}, Op) -> + lookup(T, S, Op); + +%% Leaf expressions +lookup([{dotid, "name"}|T], #'CosNotification_Property'{name=S}, Op) -> + lookup(T, S, Op); +lookup([{dotid, "value"}|T], #'CosNotification_Property'{value=S}, Op) -> + lookup(T, S, Op); + +lookup([{dotid, ID}|T], + #'CosNotification_StructuredEvent'{remainder_of_body = Any}, Op) -> + lookup(T, get_variable([], ID, Any), Op); +lookup([{dotid, ID}|T], Any, Op) -> + lookup(T, get_variable([], ID, Any), Op); + +lookup([{associd, ID}|T], S, Op) when is_list(S) -> + %% Refers to an associative array, i.e., a list of + %% #'CosNotification_Property'{name=ID, value=A} + lookup(T, get_variable(S, ID, false), Op); + +lookup([{dotint, Position}|T], S, Op) when is_record(S, any) -> + lookup(T, element(Position+2, any:get_value(S)), Op); +lookup([{dotint, Position}|T], S, Op) -> + lookup(T, element(Position+2, S), Op); + +lookup([{uint, ID}|T], S, Op) when is_record(S, any) -> + lookup([{uint, ID}|T], any:get_value(S), Op); +lookup([{uint, ID} |T], S, Op) when is_tuple(S) -> + case catch element(2, S) of + ID -> + %% The supplied union do contain the requested discriminator. + lookup(T, element(3, S), Op); + _Other when Op == exist_component -> + throw({error, {bad_id, "Bad Union ID"}}); + Other -> + %% Check if default is allowed. + M = element(1, S), + case catch M:tc() of + {tk_union,_,_,_,DefNo, UList} -> + %% {tk_union, Id, Name, DiscrTC, Default, ElementList} + case switch2alias(UList, ID) of + {ok, [], _} -> + throw({error, {bad_id, "Bad Union ID"}}); + {ok, default, _} when DefNo >= 0 -> + lookup(T, element(3, S), Op); + {ok, List, _} -> + case lists:member(Other, List) of + true -> + lookup(T, element(3, S), Op); + _-> + throw({error, {bad_id, "Bad Union ID"}}) + end + end + end + end; +lookup([{ustr, ID}|T], S, Op) when is_record(S, any) -> + lookup([{ustr, ID}|T], any:get_value(S), Op); +lookup([{ustr, ID}|T], S, Op) when is_tuple(S) -> + M = element(1, S), + case catch M:tc() of + {tk_union,_,_,_,DefNo, UList} -> + case id2switch(UList, ID) of + [default] when DefNo >= 0 -> + lookup(T, element(3, S), Op); + [default] -> + throw({error, {bad_id, "Bad Union ID supplied"}}); + Found -> + case catch lists:member(element(2, S), Found) of + true -> + lookup(T, element(3, S), Op); + _ -> + throw({error, {bad_id, "Bad Union ID supplied"}}) + end + end + end; +lookup([default|T], S, Op) when is_record(S, any) -> + lookup([default|T], any:get_value(S), Op); +lookup([default|T], S, Op) when is_tuple(S) -> + M = element(1, S), + case catch M:tc() of + {tk_union,_,_,_,DefNo, _UList} when DefNo < 0 -> + %% {tk_union, Id, Name, DiscrTC, Default, ElementList} + throw({error, {bad_id, "No default discriminator"}}); + {tk_union,_,_,_,_DefNo, UList} -> + %% {tk_union, Id, Name, DiscrTC, Default, ElementList} + %% Check if the label really is default. + case lists:keymember(element(2, S), 1, UList) of + false -> + lookup(T, element(3, S), Op); + _-> + throw({error, {bad_id, "Bad Union"}}) + end; + _-> + throw({error, {bad_id, "Bad Union"}}) + end; + +lookup([{arrindex, Index}|T], S, Op) when is_tuple(S) -> + %% The OMG uses c/c++ index. We must add one. + lookup(T, element(Index+1,S), Op); + +%%%%%%%%%%%%%%%%%%%%%%% LEAF EXPRESSIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% got '$._length', which maps to the 'remainder_of_body' +lookup(['_length'], + #'CosNotification_StructuredEvent'{remainder_of_body = Any}, _Op) -> + {ok, length(any:get_value(Any))}; +lookup(['_length'], S, _Op) when is_record(S, any) -> + {ok, length(any:get_value(S))}; +lookup(['_length'], S, _Op) when is_list(S) -> + {ok, length(S)}; +lookup(['_length'], S, _Op) when is_tuple(S) -> + {ok, length(tuple_to_list(S))}; + +%% got '$._d', which maps to the 'remainder_of_body' +%% The discriminator may, accordiong to the CORBA specification, be (2.3/p3-37): +%% * integer_type +%% * char_type +%% * boolean_type +%% * enum_type +%% * scoped_name +lookup(['_d'], + #'CosNotification_StructuredEvent'{remainder_of_body = Any}, + default_component) -> + lookup(['_d'], any:get_value(Any), default_component); +lookup(['_d'], S, default_component) when is_record(S, any) -> + lookup(['_d'], any:get_value(S), default_component); +lookup(['_d'], S, default_component) -> + M = element(1, S), + case catch M:tc() of + {tk_union,_,_,_,DefNo,_} when DefNo < 0 -> + %% '-1' indicates that no default value may exist. + {ok, false}; + {tk_union,_,_,_,_,UList} -> + %% May be using the default setting; check if the Value is in the list. + {ok, not lists:keymember(element(2, S), 1, UList)}; + _ -> + {ok, false} + end; +lookup(['_d'], + #'CosNotification_StructuredEvent'{remainder_of_body = Any}, _Op) -> + {ok, element(2, any:get_value(Any))}; +lookup(['_d'], S, _Op) when is_record(S, any) -> + {ok, element(2, any:get_value(S))}; +lookup(['_d'], S, _Op) -> + {ok, element(2, S)}; + + +lookup(['_type_id'], S, _Op) when is_record(S,'CosNotification_StructuredEvent') -> + {ok, "StructuredEvent"}; +lookup(['_type_id'], S, _Op) when is_record(S,'CosNotification_EventHeader') -> + {ok, "EventHeader"}; +lookup(['_type_id'], S, _Op) when is_record(S,'CosNotification_FixedEventHeader') -> + {ok, "FixedEventHeader"}; +lookup(['_type_id'], S, _Op) when is_record(S,'CosNotification_EventType') -> + {ok, "EventType"}; +lookup(['_type_id'], S, _Op) when is_record(S,'CosNotification_Property') -> + {ok, "Property"}; +lookup(['_type_id'], S, _Op) when is_tuple(S) -> + M=element(1, S), + Name = case catch M:tc() of + {tk_union,_,ID,_,_,_} -> + ID; + {tk_enum, _, ID, _} -> + ID; + {tk_exception, _, ID, _} -> + ID; + {tk_alias, _, ID, _} -> + ID; + {tk_struct,_,ID,_} -> + ID + end, + {ok, Name}; + +lookup(['_repos_id'], S, _Op) when is_record(S,'CosNotification_StructuredEvent') -> + {ok, 'CosNotification_StructuredEvent':id()}; +lookup(['_repos_id'], S, _Op) when is_record(S,'CosNotification_EventHeader') -> + {ok, 'CosNotification_EventHeader':id()}; +lookup(['_repos_id'], S, _Op) when is_record(S,'CosNotification_FixedEventHeader') -> + {ok, 'CosNotification_FixedEventHeader':id()}; +lookup(['_repos_id'], S, _Op) when is_record(S,'CosNotification_EventType') -> + {ok, 'CosNotification_EventType':id()}; +lookup(['_repos_id'], S, _Op) when is_record(S,'CosNotification_Property') -> + {ok, 'CosNotification_Property':id()}; +lookup(['_repos_id'], S, _Op) when is_tuple(S) -> + M = element(1, S), + {ok, M:id()}; + +lookup(_, _, _) -> + error. + + +%%------------------------------------------------------------ +%% function : locate_var +%% Arguments: Paths - A list of path-lists which tells us where +%% to search for runtime variables and in which +%% order. +%% S - Data +%% Op - se lookup/3 +%% Returns : {error, _} | +%% {ok, Val} +%%------------------------------------------------------------ +locate_var([], _S, _) -> + {error, "not found"}; +locate_var([H|T], S, Op) -> + case catch lookup(H, S, Op) of + {ok, Val} -> + {ok,Val}; + _ -> + locate_var(T, S, Op) + end. + +%%------------------------------------------------------------ +%% function : id2switch +%% Arguments: UList - The list of elements contained in the +%% Union TypeCode. +%% ID - string() eq name of element. +%% Returns : Acc - A list of switches related to given ID. +%%------------------------------------------------------------ +id2switch(UList, ID) -> + id2switch(UList, ID, [], false). +id2switch([], _, Acc, _) -> + Acc; +id2switch([{Sw, ID, _}|T], ID, Acc, _) -> + id2switch(T, ID, [Sw|Acc], true); +id2switch([_|_T], _ID, Acc, true) -> + Acc; +id2switch([_|T], ID, Acc, Found) -> + id2switch(T, ID, Acc, Found). + +%%------------------------------------------------------------ +%% function : switch2alias +%% Arguments: UList - The list of elements contained in the +%% Union TypeCode. +%% Switch - the union discriminator. +%% Returns : Acc - A list of switches that are defined with the same +%% ID - The switches common ID. +%% Comment : A union IDL code can look like: +%% union Union switch(long) { +%% case 1: +%% case 2: long ID; }; +%% In this case supplying Switch == 1 (or) the result +%% should be {ok, [1,2], "ID"} +%%------------------------------------------------------------ +switch2alias([], _Switch) -> + %% Is it really possible to define an empty union?? + {ok, [], undefined}; +switch2alias([{Sw, ID, TC}|UList], Switch) -> + switch2alias([{Sw, ID, TC}|UList], Switch, [], ID, false). + + +switch2alias([{default, ID, _}], _, _, _, false) -> + {ok, default, ID}; +switch2alias([], _, _Acc, _, false) -> + {ok, [], undefined}; +switch2alias([], _, Acc, PreviousID, _) -> + {ok, Acc, PreviousID}; + +%% Seen the ID before but just found the correct switch, e.g., +%% [... {0,"K",{tk_string,0}}, {2,"K",{tk_string,0}}...] and switch eq '2' +switch2alias([{Switch, PreviousID, _}|T], Switch, Acc, PreviousID, _Found) -> + switch2alias(T, Switch, [Switch|Acc], PreviousID, true); + +%% First time for this ID and found the correct switch +switch2alias([{Switch, ID, _}|T], Switch, _Acc, _PreviousID, false) -> + switch2alias(T, Switch, [Switch], ID, true); + +%% Seen this ID and found the correct switch before. +switch2alias([{Sw, PreviousID, _}|T], Switch, Acc, PreviousID, true) -> + switch2alias(T, Switch, [Sw|Acc], PreviousID, true); + +%% Seen this ID but not found the correct switch. +switch2alias([{Sw, PreviousID, _}|T], Switch, Acc, PreviousID, false) -> + switch2alias(T, Switch, [Sw|Acc], PreviousID, false); + +%% No more of the correct ID/Switch. Done. +switch2alias([{_, _ID, _}|_], _, Acc, PreviousID, true) -> + {ok, Acc, PreviousID}; +%% Not found correct switch and ID is updated. +switch2alias([{Sw, ID, _}|T], Switch, _Acc, _PreviousID, Found) -> + switch2alias(T, Switch, [Sw], ID, Found). + + +%%------------------------------------------------------------ +%% function : get_field +%% Arguments: ID - element name +%% List - The list of elements contained in the +%% TypeCode. +%% Returns : false | +%% offset +%%------------------------------------------------------------ +get_field(ID, List) -> + get_field(ID, List, 2). +get_field(_ID, [], _) -> + false; +get_field(ID, [ID|_], I) -> + %% Memberlists in enum. + I; +get_field(ID, [{ID,_}|_], I) -> + %% Memberlists in structs. + I; +get_field(ID, [_|T], I) -> + get_field(ID, T, I+1). + +%%------------------------------------------------------------ +%% function : check_types +%% Arguments: A sequence of CosNotification::EventType{}, i.e., +%% name-value pairs. +%% Returns : {ok, WhichType, WC} +%% WhichType - type/domain/both +%% WC - [Types using wildcard] +%%------------------------------------------------------------ +%% With check_types we try to determin if one or more EventTypes force us to check +%% all events against this constraint. For example: +%% EventType A1 has domain_name="car",type_name = "*" +%% EventType A2 has domain_name="*",type_name = "DodgeViper" +%% Since A1 says that we must test against any type_name and A2 +%% against any domain_name, we must test all events using these permutations. +%% It's better to do these test now instead of when we are up and running. But +%% if a client change the constraints VERY often it's up to them and they have +%% to accept the delay. +%%------------------------------------------------------------ + +%% If types is an empty list it means that this constraint must be used +%% for all events. +check_types([]) -> true; +check_types(Types) -> check_types(Types, both, []). +check_types([], Which, WildCard) -> {ok, Which, WildCard}; +%% The following cases means that all events matches. +check_types([#'CosNotification_EventType'{domain_name="",type_name = ""}|_T],_,_) -> + true; +check_types([#'CosNotification_EventType'{domain_name="",type_name = "*"}|_T],_,_) -> + true; +check_types([#'CosNotification_EventType'{domain_name="*",type_name = ""}|_T],_,_) -> + true; +check_types([#'CosNotification_EventType'{domain_name="*",type_name = "*"}|_T],_,_) -> + true; +%% The following cases means that all events must be tested using this constraint. +check_types([#'CosNotification_EventType'{domain_name="",type_name = Ty}|T], domain,WC) when is_list(Ty) -> + check_wildcard(T, all, WC, "", Ty); +check_types([#'CosNotification_EventType'{domain_name="*",type_name = Ty}|T], domain, WC) when is_list(Ty) -> + check_wildcard(T, all, WC, "", Ty); +check_types([#'CosNotification_EventType'{domain_name=Do,type_name = ""}|T], type,WC) when is_list(Do) -> + check_wildcard(T, all, WC, Do, ""); +check_types([#'CosNotification_EventType'{domain_name=Do,type_name = "*"}|T], type,WC) when is_list(Do) -> + check_wildcard(T, all, WC, Do, ""); +%% The following cases is used to prevent other cases from converting, +%% for example, all->type. +check_types([#'CosNotification_EventType'{domain_name="",type_name = Ty}|T], all,WC) when is_list(Ty) -> + check_wildcard(T, all, WC, "", Ty); +check_types([#'CosNotification_EventType'{domain_name="*",type_name = Ty}|T], all,WC) when is_list(Ty) -> + check_wildcard(T, all, WC, "", Ty); +check_types([#'CosNotification_EventType'{domain_name=Do,type_name = ""}|T], all,WC) when is_list(Do) -> + check_wildcard(T, all, WC, Do, ""); +check_types([#'CosNotification_EventType'{domain_name=Do,type_name = "*"}|T], all,WC) when is_list(Do) -> + check_wildcard(T, all, WC, Do, ""); +%% The following cases means that all events with matching Type must be +%% tested using this constraint. +check_types([#'CosNotification_EventType'{domain_name="",type_name = Ty}|T], _W,WC) when is_list(Ty) -> + check_wildcard(T, type, WC, "", Ty); +check_types([#'CosNotification_EventType'{domain_name="*",type_name = Ty}|T], _W,WC) when is_list(Ty) -> + check_wildcard(T, type, WC, "", Ty); +%% The following cases means that all events with matching Domain must be +%% tested using this constraint. +check_types([#'CosNotification_EventType'{domain_name=Do,type_name = ""}|T], _W,WC) when is_list(Do) -> + check_wildcard(T, domain, WC, Do, ""); +check_types([#'CosNotification_EventType'{domain_name=Do,type_name = "*"}|T], _W,WC) when is_list(Do) -> + check_wildcard(T, domain, WC, Do, ""); +%% Sorry, no shortcuts. +check_types([#'CosNotification_EventType'{domain_name=Do,type_name=Ty}|T], W,WC) when is_list(Do) andalso is_list(Ty) -> + check_wildcard(T, W, WC, Do, Ty); +check_types([H|_], _,_) when is_record(H, 'CosNotification_EventType') -> + %% Not valid. + corba:raise(#'CosNotifyComm_InvalidEventType'{type=H}); +check_types(_,_,_) -> + %% Wasn't even a correct input. + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + +check_wildcard(Types, Which, WC, Domain, Type) -> + NewWC = + case {string:chr(Domain, $*), string:chr(Type, $*)} of + {0, 0} -> + WC; + {0, _}-> + [{type, Domain, convert_wildcard(Type, [])}|WC]; + {_, 0}-> + [{domain, convert_wildcard(Domain, []), Type}|WC]; + _-> + [{both, convert_wildcard(Domain, []), convert_wildcard(Type, [])}|WC] + end, + check_types(Types, Which, NewWC). + +%% Change '*' to '.*', see regexp:parse/2 documentation. +convert_wildcard([], Acc) -> + case regexp:parse(lists:reverse(Acc)) of + {ok, Expr} -> + Expr; + _ -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end; +convert_wildcard([$*|T], Acc) -> + convert_wildcard(T, [$*, $.|Acc]); +convert_wildcard([H|T], Acc) -> + convert_wildcard(T, [H|Acc]). + +%%------------------------------------------------------------ +%% function : match_types +%% Arguments: A sequence of {Which, Domain, Type}, i.e., the same as +%% returned from cosNotification_Filter:check_types/3 +%% Returns : bolean() +%%------------------------------------------------------------ +match_types(_, _, []) -> + false; +match_types(Domain, Type, [{domain, WCDomain, Type}|T]) -> + L=length(Domain), + case catch regexp:matches(Domain, WCDomain) of + {match, []} -> + match_types(Domain, Type, T); + {match, [{1, L}]} -> + true; + _-> + match_types(Domain, Type, T) + end; +match_types(Domain, Type, [{type, Domain, WCType}|T]) -> + L=length(Type), + case catch regexp:matches(Type, WCType) of + {match, []} -> + match_types(Domain, Type, T); + {match, [{1, L}]} -> + true; + _-> + match_types(Domain, Type, T) + end; +match_types(Domain, Type, [{both, WCDomain, WCType}|T]) -> + L1=length(Domain), + case catch regexp:matches(Domain, WCDomain) of + {match, []} -> + match_types(Domain, Type, T); + {match, [{1, L1}]} -> + L2=length(Type), + case catch regexp:matches(Type, WCType) of + {match, []} -> + match_types(Domain, Type, T); + {match, [{1, L2}]} -> + true; + _-> + match_types(Domain, Type, T) + end; + _-> + match_types(Domain, Type, T) + end; +match_types(Domain, Type, [_|T]) -> + match_types(Domain, Type, T). + +%%------------------------------------------------------------ +%% function : validate_types +%% Arguments: A sequence of CosNotification::EventType{}, i.e., +%% name-value pairs. +%% Returns : ok | +%% {'EXCEPTION', #'CosNotifyComm_InvalidEventType'{}} +%%------------------------------------------------------------ + +validate_types([]) -> + ok; +validate_types([#'CosNotification_EventType'{domain_name=Do,type_name=Ty}|T]) + when is_list(Do) andalso is_list(Ty) -> + validate_types(T); +validate_types([H|_]) + when is_record(H, 'CosNotification_EventType') -> + %% Not valid. + corba:raise(#'CosNotifyComm_InvalidEventType'{type=H}); +validate_types(_) -> + %% Wasn't even a correct input. + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}). + + +%%--------------- END OF MODULE ------------------------------ diff --git a/lib/cosNotification/src/cosNotification_Grammar.yrl b/lib/cosNotification/src/cosNotification_Grammar.yrl new file mode 100644 index 0000000000..98233bf92d --- /dev/null +++ b/lib/cosNotification/src/cosNotification_Grammar.yrl @@ -0,0 +1,166 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%-------------------------------------------------------------------- +%% File : cosNotification_Grammar.yrl +%% Purpose : Implement the constraint grammar for CosNotification filters. +%%-------------------------------------------------------------------- + +Nonterminals + '<toplevel>' '<constraint>' '<expr>' '<bool>' '<bool_or>' '<Ident>' + '<bool_and>' '<bool_compare>' '<expr_in>' '<expr_twiddle>' '<term>' + '<factor_not>' '<factor>' '<Component>' '<CompExt>' '<CompDot>' '<UnionVal>'. + +Terminals +% 'dbslsh' 'bslshd' + 'bslsh' 'ident' 'string' + '_length' '_d''_type_id' '_repos_id' + 'not' 'or' 'and' 'num' + 'in' '~' '.' 'dollar' + 'ADDOP' 'RELOP' 'MULOP' 'default' 'exist' + 'TRUE' 'FALSE' + '(' ')' '[' ']' 'int'. + +Left 100 'or'. +Left 200 'and'. +%Nonassoc 300 'RELOP'. % '==', '!=', '<', '>', '<=', '=>' +Left 300 'RELOP'. +%Nonassoc 400 'in'. +Left 400 'in'. +%Nonassoc 500 '~'. +Left 500 '~'. +Left 600 'ADDOP'. % '+', '-' +Left 700 'MULOP'. % '*', '/' +Unary 800 'not'. +Unary 900 'exist'. +Unary 900 'default'. +%Unary 900 'u-'. % unary minus + +Rootsymbol '<toplevel>'. +Endsymbol '$end'. + +'<toplevel>' -> '$empty' : '$empty'. +'<toplevel>' -> '<constraint>' : '$1'. + +'<constraint>' -> '<bool>' : '$1'. + +'<bool>' -> '<bool_or>' : '$1'. + +'<bool_or>' -> '<bool_or>' 'or' '<bool_and>' : {'or', '$1', '$3'}. +'<bool_or>' -> '<bool_and>' : '$1'. + +'<bool_and>' -> '<bool_and>' 'and' '<bool_compare>' : {'and', '$1', '$3'}. +'<bool_and>' -> '<bool_compare>' : '$1'. + +'<bool_compare>' -> '<expr_in>' 'RELOP' '<expr_in>' : {element(2, '$2'), '$1', '$3'}. +'<bool_compare>' -> '<expr_in>' : '$1'. + +'<expr_in>' -> '<expr_twiddle>' : '$1'. +'<expr_in>' -> '<expr_twiddle>' 'in' '<Ident>' : {'in', '$1', '$3'}. +'<expr_in>' -> '<expr_twiddle>' 'in' 'dollar' '<Component>' : {'in', '$1', examin_comp({'component', '$4'})}. + +'<expr_twiddle>' -> '<expr>' : '$1'. +'<expr_twiddle>' -> '<expr>' '~' '<expr>' : {'~', '$1', '$3'}. + +'<expr>' -> '<term>' : '$1'. +'<expr>' -> '<expr>' 'ADDOP' '<term>' : {element(2, '$2'), '$1', '$3'}. + +'<term>' -> '<factor_not>' : '$1'. +'<term>' -> '<term>' 'MULOP' '<factor_not>' : {element(2, '$2'), '$1', '$3'}. + +'<factor_not>' -> '<factor>' : '$1'. +'<factor_not>' -> 'not' '<factor>' : {'not', '$2'}. + +'<factor>' -> '(' '<bool_or>' ')' : '$2'. +'<factor>' -> 'num' : element(2, '$1'). +'<factor>' -> 'int' : element(2, '$1'). +'<factor>' -> 'string' : element(2, '$1'). +'<factor>' -> 'TRUE' : 'true'. +'<factor>' -> 'FALSE' : 'false'. +'<factor>' -> 'ADDOP' 'num' : create_unary(element(2, '$1'), element(2, '$2')). +'<factor>' -> 'ADDOP' 'int' : create_unary(element(2, '$1'), element(2, '$2')). +'<factor>' -> '<Ident>' : list_to_atom('$1'). +'<factor>' -> 'dollar' '<Component>' : examin_comp({component, '$2'}). +'<factor>' -> 'default' 'dollar' '<Component>' : examin_comp({'default_component', '$3'}). +'<factor>' -> 'exist' 'dollar' '<Component>' : examin_comp({'exist_component', '$3'}). + +%% The following rules are used to create Components. The format used is: +%% [...] +'<Component>' -> '.' '<CompDot>' : '$2'. +'<Component>' -> '[' 'int' ']' '<CompExt>' : [{'arrindex', element(2, '$2')} | '$4']. %% CompArray +'<Component>' -> '(' '<Ident>' ')' '<CompExt>' : [{'associd', '$2'} | '$4']. %%CompAssoc +'<Component>' -> '<Ident>' '<CompExt>' : [{'varid', '$1'} | '$2']. %% run-time variable +'<Component>' -> '$empty' : []. + +'<CompExt>' -> '.' '<CompDot>' : '$2'. +'<CompExt>' -> '[' 'int' ']' '<CompExt>' : [{'arrindex', element(2, '$2')} | '$4']. %% CompArray +'<CompExt>' -> '(' '<Ident>' ')' '<CompExt>' : [{'associd', '$2'} | '$4']. %%CompAssoc +'<CompExt>' -> '$empty' : []. + +'<CompDot>' -> '<Ident>' '<CompExt>' : [{'dotid', '$1'} | '$2']. +'<CompDot>' -> 'int' '<CompExt>' : [{'dotint', element(2, '$1')} | '$2']. %% ComPos +'<CompDot>' -> '(' '<UnionVal>' ')' '<CompExt>' : ['$2' | '$4']. %% UnionPos +'<CompDot>' -> '_length' : ['_length']. %% arrays or sequences ONLY +'<CompDot>' -> '_d' : ['_d']. %% discriminated unions ONLY +'<CompDot>' -> '_type_id' : ['_type_id']. %% ok if info can be obtained +'<CompDot>' -> '_repos_id' : ['_repos_id']. %% ok if info can be obtained + +'<Ident>' -> 'ident' : element(2, '$1'). +'<Ident>' -> 'bslsh' 'ident' : element(2, '$2'). + +'<UnionVal>' -> 'int' : {'uint', element(2, '$1')}. +'<UnionVal>' -> 'ADDOP' 'int' : {'uint', create_unary(element(2, '$1'), element(2, '$2'))}. +'<UnionVal>' -> 'string' : {'ustr', element(2, '$1')}. +'<UnionVal>' -> '$empty': 'default'. + +Erlang code. +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% File : cosNotification_Grammar.erl +%% Purpose : THIS FILE HAS BEEN GENERATED. DO NOT EDIT!!!! +%%---------------------------------------------------------------------- + +-include("CosNotification_Definitions.hrl"). + +create_unary('+', Val) when is_number(Val) -> Val; +create_unary('-', Val) when is_number(Val) -> -Val; +create_unary(_, _) -> return_error(0, "syntax error"). + +examin_comp({T, []}) -> + {T, '$empty'}; +examin_comp(V) -> + V. + diff --git a/lib/cosNotification/src/cosNotification_Scanner.erl b/lib/cosNotification/src/cosNotification_Scanner.erl new file mode 100644 index 0000000000..e9c54319f0 --- /dev/null +++ b/lib/cosNotification/src/cosNotification_Scanner.erl @@ -0,0 +1,268 @@ +%%---------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : cosNotification_Scanner.erl +%% Purpose : Scan and pre-process a grammar. +%%---------------------------------------------------------------------- + +-module('cosNotification_Scanner'). + +-export([scan/1]). + +scan(Str) -> + RSL = scan(Str, 1, [], any), + {ok, lists:reverse(RSL)}. + + +%% Guard macros used at top scan functions only +-define(is_number(X), X >= $0, X =< $9). +-define(is_upper(X), X >= $A, X =< $Z). +-define(is_lower(X), X >= $a, X =< $z). + +%%---------------------------------------------------------------------- +%% scan +%% +%% A-Z +scan([X|Str], Line, Out, Type) when ?is_upper(X) -> + scan_name(Str, [X], Line, Out, Type); +%% a-z +scan([X|Str], Line, Out, Type) when ?is_lower(X) -> + scan_name(Str, [X], Line, Out, Type); +%% 0-9 +scan([X|Str], Line, Out, Type) when ?is_number(X) -> + scan_number(Str, [X], Line, Out, Type); + +%% RELOP:s == != <= >= > < +scan([$=,$= | Str], Line, Out, _Type) -> + scan(Str, Line, [{'RELOP', '=='} | Out], any); +scan([$!,$= | Str], Line, Out, _Type) -> + scan(Str, Line, [{'RELOP', '!='} | Out], any); +scan([$<,$= | Str], Line, Out, _Type) -> + scan(Str, Line, [{'RELOP', '<='} | Out], any); +scan([$>,$= | Str], Line, Out, _Type) -> + scan(Str, Line, [{'RELOP', '>='} | Out], any); +scan([$> | Str], Line, Out, _Type) -> + scan(Str, Line, [{'RELOP', '>'} | Out], any); +scan([$< | Str], Line, Out, _Type) -> + scan(Str, Line, [{'RELOP', '<'} | Out], any); + +%% ADDOP:s + - +scan([$+ | Str], Line, Out, Type) -> + scan(Str, Line, [{'ADDOP', '+'} | Out], Type); +scan([$- | Str], Line, Out, Type) -> + scan(Str, Line, [{'ADDOP', '-'} | Out], Type); + +%% MULOP:s * / +scan([$* | Str], Line, Out, _Type) -> + scan(Str, Line, [{'MULOP', '*'} | Out], any); +scan([$/ | Str], Line, Out, _Type) -> + scan(Str, Line, [{'MULOP', '/'} | Out], any); + +%% TAB +scan([9| T], Line, Out, Type) -> scan(T, Line, Out, Type); +%% SP +scan([32| T], Line, Out, Type) -> scan(T, Line, Out, Type); +%% CR +scan([$\r|Str], Line, Out, Type) -> + scan(Str, Line, Out, Type); +%% LF +scan([$\n|Str], Line, Out, Type) -> + scan(Str, Line+1, Out, Type); +%% \\ +scan([92, 92 | Str], Line, Out, Type) -> + scan(Str, Line, [{'dbslsh', Line} | Out], Type); +%% \' +scan([92, 39 | Str], Line, Out, Type) -> + scan(Str, Line, [{'bslshd', Line} | Out], Type); +%% '\' +scan([92 | Str], Line, Out, Type) -> + scan(Str, Line, [{'bslsh', Line} | Out], Type); +%% '_' +scan([$_ | Str], Line, Out, dollar) -> + scan_name(Str, [$_], Line, Out, dollar); +%% '$' +scan([$$, 92 | Str], Line, Out, _Type) -> + scan(Str, Line, [{'bslsh', Line}, {'dollar', Line} | Out], dollar); +scan([$$ | Str], Line, Out, _Type) -> + scan(Str, Line, [{'dollar', Line} | Out], dollar); +scan([$"|Str], Line, Out, Type) -> + scan_const(char, Str, [], Line, Out, Type); +scan([$'|Str], Line, Out, Type) -> + scan_const(string, Str, [], Line, Out, Type); + +%% Writing '+.<CompDot>' is not allowed ('+' or '-' are only allowed +%% as unary for <UnionVal> (within a component) which must be en integer). +scan([$. | Str], Line, [{'ADDOP', Op}|Out], _) -> + scan_frac(Str, [$.], Line, [{'ADDOP', Op}|Out], any); +%% Must be a <CompDot> +scan([$. | Str], Line, Out, dollar) -> + scan(Str, Line, [{'.',Line} | Out], dollar); +%% Number +scan([$. | Str], Line, Out, Type) -> + scan_frac(Str, [$.], Line, Out, Type); +scan([C|Str], Line, Out, Type) -> + scan(Str, Line, [{list_to_atom([C]), Line} | Out], Type); + +scan([], _Line, Out, _Type) -> + Out. + +%%---------------------------------------------------------------------- +%% scan_name +%% + +scan_number([X|Str], Accum, Line, Out, Type) when ?is_number(X) -> + scan_number(Str, [X|Accum], Line, Out, Type); +scan_number([X|Str], Accum, Line, Out, dollar) when X==$. -> + scan(Str, Line, [{'.', Line}, + {'int', list_to_integer(lists:reverse(Accum))} | Out], dollar); +scan_number([X|Str], Accum, Line, Out, Type) when X==$. -> + scan_frac(Str, [X|Accum], Line, Out, Type); +scan_number([X|Str], Accum, Line, Out, Type) when X==$e -> + scan_exp(Str, [X|Accum], Line, Out, Type); +scan_number([X|Str], Accum, Line, Out, Type) when X==$E -> + scan_exp(Str, [X|Accum], Line, Out, Type); +scan_number(Str, Accum, Line, Out, Type) -> + scan(Str, Line, [{'int', list_to_integer(lists:reverse(Accum))} | Out], Type). + + +%% Floating point number scan. +%% +%% Non trivial scan. A float consists of an integral part, a +%% decimal point, a fraction part, an e or E and a signed integer +%% exponent. Either the integer part or the fraction part but not +%% both may be missing, and either the decimal point or the +%% exponent part but not both may be missing. The exponent part +%% must consist of an e or E and a possibly signed exponent. +%% +%% Analysis shows that "1." ".7" "1e2" ".5e-3" "1.7e2" "1.7e-2" +%% is allowed and "1" ".e9" is not. The sign is only allowed just +%% after an e or E. The scanner reads a number as an integer +%% until it encounters a "." so the integer part only error case +%% will not be caught in the scanner (but rather in expression +%% evaluation) + +scan_frac([$e | _Str], [$.], _Line, _Out, _Type) -> + {error, "illegal_float"}; +scan_frac([$E | _Str], [$.], _Line, _Out, _Type) -> + {error, "illegal_float"}; +scan_frac(Str, Accum, Line, Out, Type) -> + scan_frac2(Str, Accum, Line, Out, Type). + +scan_frac2([X|Str], Accum, Line, Out, Type) when ?is_number(X) -> + scan_frac2(Str, [X|Accum], Line, Out, Type); +scan_frac2([X|Str], Accum, Line, Out, Type) when X==$e -> + scan_exp(Str, [X|Accum], Line, Out, Type); +scan_frac2([X|Str], Accum, Line, Out, Type) when X==$E -> + scan_exp(Str, [X|Accum], Line, Out, Type); +%% Since '.2' is allowed, we add '0' in front to be sure (erlang do not allow +%% list_to_float(".2") and list_to_float("0.2") eq. list_to_float("00.2")). +scan_frac2(Str, Accum, Line, Out, Type) -> + scan(Str, Line, [{'num', list_to_float([$0|lists:reverse(Accum)])} | Out], Type). + +scan_exp([X|Str], Accum, Line, Out, Type) when X==$- -> + scan_exp2(Str, [X|Accum], Line, Out, Type); +scan_exp(Str, Accum, Line, Out, Type) -> + scan_exp2(Str, Accum, Line, Out, Type). + +scan_exp2([X|Str], Accum, Line, Out, Type) when ?is_number(X) -> + scan_exp2(Str, [X|Accum], Line, Out, Type); +%% Since '.2' is allowed, we add '0' in front to be sure (erlang do not allow +%% list_to_float(".2")). +scan_exp2(Str, Accum, Line, Out, Type) -> + scan(Str, Line, [{'num', list_to_float([$0|lists:reverse(Accum)])} | Out], Type). + + +scan_name([X|Str], Accum, Line, Out, Type) when ?is_upper(X) -> + scan_name(Str, [X|Accum], Line, Out, Type); +scan_name([X|Str], Accum, Line, Out, Type) when ?is_lower(X) -> + scan_name(Str, [X|Accum], Line, Out, Type); +scan_name([X|Str], Accum, Line, Out, Type) when ?is_number(X) -> + scan_name(Str, [X|Accum], Line, Out, Type); +scan_name([$_|Str], Accum, Line, Out, dollar) -> + scan_name(Str, [$_|Accum], Line, Out, dollar); +scan_name(S, Accum, Line, [{bslsh,LL} | Out], Type) -> + %% An escaped identifier. + L = lists:reverse(Accum), + scan(S, Line, [{'ident', L}, {bslsh,LL} | Out], Type); +scan_name(S, Accum, Line, Out, Type) -> + L = lists:reverse(Accum), + {X, NewType} = case check_name(L) of + false -> + {{'ident', L}, Type}; + _ -> + {{list_to_atom(L), Line}, any} + end, + scan(S, Line, [X | Out], NewType). + +%% Shall scan a constant +scan_const(char, [$" | Rest], Accum, Line, Out, Type) -> + scan(Rest, Line, + [{'ident', list_to_atom(lists:reverse(Accum))} | Out], Type); +scan_const(char, [], _Accum, _Line, Out, _Type) -> %% Bad string +% {error, "bad_string"}; + Out; +scan_const(string, [$' | Rest], Accum, Line, Out, Type) -> + scan(Rest, Line, + [{'string', lists:reverse(Accum)} | Out], Type); +scan_const(Mode, [$\\, C | Rest], Accum, Line, Out, Type) -> + case escaped_char(C) of + error -> + %% Bad escape character + %% {error, "bad_escape_character"}; + scan_const(Mode, Rest, [C | Accum], Line, Out, Type); + EC -> + scan_const(Mode, Rest, [EC | Accum], Line, Out, Type) + end; +scan_const(Mode, [C | Rest], Accum, Line, Out, Type) -> + scan_const(Mode, Rest, [C | Accum], Line, Out, Type). + +%% Escaped character. Escaped chars are repr as two characters in the +%% input list of letters and this is translated into one char. +escaped_char($n) -> $\n; +escaped_char($t) -> $\t; +escaped_char($v) -> $\v; +escaped_char($b) -> $\b; +escaped_char($r) -> $ ; +escaped_char($f) -> $\f; +escaped_char($a) -> $\a; +escaped_char($\\) -> $\\; +escaped_char($?) -> $?; +escaped_char($') -> $'; +escaped_char($") -> $"; +%% Error +escaped_char(_Other) -> error. + + +check_name("exist") -> true; +check_name("default") -> true; +check_name("_length") -> true; +check_name("_d") -> true; +check_name("_type_id") -> true; +check_name("_repos_id") -> true; +check_name("not") -> true; +check_name("or") -> true; +check_name("and") -> true; +check_name("FALSE") -> true; +check_name("TRUE") -> true; +check_name("in") -> true; +check_name(_) -> false. + + diff --git a/lib/cosNotification/src/cosNotification_eventDB.erl b/lib/cosNotification/src/cosNotification_eventDB.erl new file mode 100644 index 0000000000..89332d53f2 --- /dev/null +++ b/lib/cosNotification/src/cosNotification_eventDB.erl @@ -0,0 +1,1350 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : cosNotification_eventDB.erl +%% Purpose : +%% Purpose : This module is supposed to centralize Event storage. +%% Comments: +%% * Setting Order Policy to AnyOrder eq. Priority order +%% +%% * Setting Discard Policy to AnyOrder eq. RejectNewEvents. +%% +%% * DB ordering: Since the deliver- and discard-order may differ we need +%% two ets-tables, both of type 'ordered_set'. They contain: +%% - table 1 (T1): deliver order key and the associated event. +%% - table 2 (T2): discard order key. +%% +%% When adding a new event we add, if necessary, related keys in T2. +%% For example, if we should discard events in FIFO order, the delivery +%% order may be set to Priority order. If the Max Event limit is reached +%% we first look in T2 to find out which event to discard by using and +%% reorder the key elements. T2 gives {TimeStamp, Priority}, which is used +%% to lookup in T1 as {Priority, TimeStamp}. +%% A TimeStamp is always included in the DB keys, even if FIFO or LIFO +%% is used, since lots of events probably will have the same prioity and +%% with a little bit of bad luck some events will never be delivered. +%% +%% Note: deliver order AnyOrder and PriorityOrder is equal since the later +%% is defined as default. +%% discard order AnyOrder and RejectNewEvents is equal since the later +%% is defined as default. +%% The keys used is ('-' indicates T2 is not needed and, thus, not instantiated): +%% +%% T1 policy T1 Key T2 Policy T2 Key +%% ------------------------------------------------------------------ +%% DeadlineOrder {DL, Key, Prio} PriorityOrder {Prio, Key, DL} +%% DeadlineOrder {DL, Key} FifoOrder {Key, DL} +%% DeadlineOrder {DL, Key} LifoOrder {Key, DL} +%% DeadlineOrder {DL, Key} RejectNewEvents - +%% DeadlineOrder {DL, Key} DeadlineOrder - +%% FifoOrder {Key, DL} DeadlineOrder {DL, Key} +%% FifoOrder {Key, Prio} PriorityOrder {Prio, Key} +%% FifoOrder Key RejectNewEvents - +%% FifoOrder Key Fifo - +%% FifoOrder Key Lifo - +%% PriorityOrder {Prio, Key, DL} DeadlineOrder {DL, Key, Prio} +%% PriorityOrder {Prio, Key} Fifo {Key, Prio} +%% PriorityOrder {Prio, Key} Lifo {Key, Prio} +%% PriorityOrder {Prio, Key} RejectNewEvents - +%% ------------------------------------------------------------------ +%% DL == Deadline, Key == TimeStamp, Prio == Priority +%% +%% NOTE: If defined, the Discard DB Keys are the same as in Event DB, except +%% that the first and last Key change place. {K1,K2}<->{K2,K1} and +%% {K1,K2,K3}<->{K3,K2,K1}. +%%---------------------------------------------------------------------- + +-module(cosNotification_eventDB). + + +%%--------------- INCLUDES ----------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/include/ifr_types.hrl"). +-include_lib("cosTime/include/TimeBase.hrl"). + +%% Application files +-include("CosNotification.hrl"). +-include("CosNotifyChannelAdmin.hrl"). +-include("CosNotifyComm.hrl"). +-include("CosNotifyFilter.hrl"). + +-include("CosNotification_Definitions.hrl"). + +%%--------------- EXPORTS ------------------------------------ +%% Internal Filter Functions +-export([validate_event/5, + create_db/4, + destroy_db/1, + get_event/1, + get_event/2, + get_events/2, + get_events/3, + delete_events/1, + update/2, + update/4, + add_event/2, + add_event/4, + add_and_get_event/2, + add_and_get_event/3, + add_and_get_event/4, + add_and_get_event/5, + gc_events/2, + gc_events_local/4, + gc_start/2, + filter_events/2, + filter_events/3, + status/2]). + +%%--------------- DATA STRUCTURES ---------------------------- +-record(dbRef, {orderRef, discardRef, orderPolicy, discardPolicy, + defPriority, maxEvents, defStopT, startTsupport, + stopTsupport, gcTime, gcLimit, timeRef}). + + + +%%--------------- DEFINES ------------------------------------ + +-define(CreateRef(OR, DR, O, D, DP, ME, DS, StaT, StoT, GT, GL, TR), + #dbRef{orderRef=OR, discardRef=DR, orderPolicy=O, discardPolicy=D, + defPriority=DP, maxEvents=ME, defStopT=DS, startTsupport=StaT, + stopTsupport=StoT, gcTime=GT, gcLimit=round(ME*GL/100), + timeRef=TR}). + + +-define(get_OrderP(DR), DR#dbRef.orderPolicy). +-define(get_DiscardP(DR), DR#dbRef.discardPolicy). +-define(get_OrderRef(DR), DR#dbRef.orderRef). +-define(get_DiscardRef(DR), DR#dbRef.orderRef). +-define(get_DefPriority(DR), DR#dbRef.defPriority). +-define(get_MaxEvents(DR), DR#dbRef.maxEvents). +-define(get_DefStopT(DR), DR#dbRef.defStopT). +-define(get_StartTsupport(DR), DR#dbRef.startTsupport). +-define(get_StopTsupport(DR), DR#dbRef.stopTsupport). +-define(get_GCTime(DR), DR#dbRef.gcTime). +-define(get_GCLimit(DR), DR#dbRef.gcLimit). +-define(get_TimeRef(DR), DR#dbRef.timeRef). + +-define(set_OrderP(DR, O), DR#dbRef{orderPolicy = O}). +-define(set_DiscardP(DR, D), DR#dbRef{discardPolicy = D}). +-define(set_OrderRef(DR, E), DR#dbRef{orderRef = E}). +-define(set_DiscardRef(DR, E), DR#dbRef{orderRef = E}). +-define(set_DefPriority(DR, DP), DR#dbRef{defPriority = DP}). +-define(set_MaxEvents(DR, ME), DR#dbRef{maxEvents = ME}). +-define(set_DefStopT(DR, DS), DR#dbRef{defStopT = DS}). +-define(set_StartTsupport(DR, B), DR#dbRef{startTsupport = B}). +-define(set_StopTsupport(DR, B), DR#dbRef{stopTsupport = B}). + +-define(is_StartTNotSupported(DR), DR#dbRef.startTsupport == false). +-define(is_StopTNotSupported(DR), DR#dbRef.stopTsupport == false). +-define(is_TimeoutNotUsed(DR), DR#dbRef.defStopT == 0). + + +%%------------------------------------------------------------ +%% function : status +%% Arguments: DBRef +%% Key - which information we want. +%% Returns : Data related to the Key. +%%------------------------------------------------------------ +status(DBRef, eventCounter) -> + ets:info(?get_OrderRef(DBRef), size); +status(DBRef, {batchLimit, Limit}) -> + case ets:info(?get_OrderRef(DBRef), size) of + Current when is_integer(Current) andalso Current >= Limit -> + ?debug_print("BATCH LIMIT (~p) REACHED, CONTAINS: ~p~n", [Limit, Current]), + true; + _Other -> + ?debug_print("BATCH LIMIT (~p) NOT REACHED, CONTAINS: ~p~n", + [Limit, _Other]), + false + end; +status(DBRef, {batchLimit, Limit, TemporaryMax}) -> + case ets:info(?get_OrderRef(DBRef), size) of + Current when is_integer(Current) andalso Current >= TemporaryMax -> + ?debug_print("MAX LIMIT (~p) REACHED, CONTAINS: ~p~n", + [TemporaryMax, Current]), + true; + Current when is_integer(Current) andalso Current >= Limit -> + ?debug_print("BATCH LIMIT (~p) REACHED, CONTAINS: ~p~n", [Limit, Current]), + true; + _Other -> + ?debug_print("BATCH LIMIT (~p) NOT REACHED, CONTAINS: ~p~n", + [Limit, _Other]), + false + end; +status(_, _) -> + error. + + +%%------------------------------------------------------------ +%% function : gc_events_local +%% Arguments: DBRef +%% Returns : +%% Comment : This function is intended for "emergency" GC, i.e., +%% when the DB must discard events we should first try +%% to remove events with expired deadlines. +%%------------------------------------------------------------ +gc_events_local(_, _, false, _) -> + ok; +gc_events_local(_, _, _, 0) -> + ok; +gc_events_local(ORef, DRef, _, _) -> + gc_loop(ets:first(ORef), ORef, DRef). + +%%------------------------------------------------------------ +%% function : gc_events +%% Arguments: DBRef +%% Priority - 'low', 'medium' or 'high'; will determine +%% how important a fast gc is. +%% Returns : +%% Comment : This function is intended for background GC. +%%------------------------------------------------------------ +gc_events(DBRef, _Priority) when ?is_TimeoutNotUsed(DBRef) -> + ok; +gc_events(DBRef, _Priority) when ?is_StopTNotSupported(DBRef) -> + ok; +gc_events(DBRef, Priority) -> + {M,S,U} = now(), + case get(oe_GC_timestamp) of + Num when {M,S,U} > Num -> + put(oe_GC_timestamp, {M,S+?get_GCTime(DBRef),U}), + spawn_link(?MODULE, gc_start, [DBRef, Priority]); + _-> + ok + end. + + +%%------------------------------------------------------------ +%% function : gc_start +%% Arguments: +%% Returns : +%%------------------------------------------------------------ +gc_start(#dbRef{orderRef = ORef, discardRef = DRef}, Priority) -> + process_flag(priority, Priority), + gc_loop(ets:first(ORef), ORef, DRef). + +gc_loop('$end_of_table', _, _) -> + ok; +gc_loop(Key, ORef, DRef) -> + [{Keys,DL,_,_,_}]=ets:lookup(ORef, Key), + case check_deadline(DL) of + true when DRef == undefined -> + ets:delete(ORef, Key); + true -> + ets:delete(ORef, Key), + gc_discard_DB(Keys, DRef); + _ -> + ok + end, + gc_loop(ets:next(ORef, Key), ORef, DRef). + +gc_discard_DB({Key1, Key2}, DRef) -> + ets:delete(DRef, {Key2, Key1}); +gc_discard_DB({Key1, Key2, Key3}, DRef) -> + ets:delete(DRef, {Key3, Key2, Key1}). + +%%------------------------------------------------------------ +%% function : create_FIFO_Key +%% Arguments: +%% Returns : +%%------------------------------------------------------------ +create_FIFO_Key() -> + {M, S, U} = erlang:now(), + -M*1000000000000 - S*1000000 - U. + +%%------------------------------------------------------------ +%% function : convert_FIFO_Key +%% Arguments: +%% Returns : A now tuple +%% Comment : Used when we must reuse a timestamp, i.e., only +%% when we must reorder the DB. +%%------------------------------------------------------------ +convert_FIFO_Key(Key) -> + K = abs(Key), + Secs = trunc(K/1000000), + M = trunc(K/1000000000000), + S = Secs-M*1000000, + U = K - S*1000000-M*1000000000000, + {M, S, U}. + +%%------------------------------------------------------------ +%% function : extract_priority +%% Arguments: Event +%% Defalt Value +%% Mapping Filter Value +%% - false value not needed (depends on QoS type) +%% - undefined value needed but no filter associated. +%% Returns : +%%------------------------------------------------------------ +extract_priority(_, _, false) -> + false; +extract_priority(#'CosNotification_StructuredEvent' + {header = #'CosNotification_EventHeader' + {variable_header = VH}}, DefPriority, undefined) -> + extract_value(VH, ?not_Priority, DefPriority); +%% Maybe a unstructured event. +extract_priority(_, DefPriority, undefined) -> + DefPriority; +extract_priority(_, _, PriorityOverride) -> + %% Must have an associated MappingFilter for Priority. + PriorityOverride. + +%%------------------------------------------------------------ +%% function : extract_start_time +%% Arguments: +%% Returns : +%%------------------------------------------------------------ +extract_start_time(_, false, _) -> + false; +extract_start_time(#'CosNotification_StructuredEvent' + {header = #'CosNotification_EventHeader' + {variable_header = VH}}, _, TRef) -> + ST = case extract_value(VH, ?not_StartTime, undefined) of + UTC when is_record(UTC, 'TimeBase_UtcT') -> + UTC; + _ -> + false + end, + convert_time(ST, TRef, now()); +extract_start_time(_, _, _) -> + false. + +%%------------------------------------------------------------ +%% function : extract_deadline +%% Arguments: Structured Event +%% Default Timeout Value - TimeT or UtcT (see cosTime). +%% StopTSupported - boolean(). +%% TRef - reference to TimeService +%% Mapping Filter Value +%% - false eq. value not needed (depends on QoS type) +%% - undefined eq. value needed but no filter associated. +%% Now - used when we want to reuse old TimeStamp which +%% must be done when changing QoS. +%% Returns : A modified return from now(). +%%------------------------------------------------------------ +extract_deadline(_, _, _, _, false) -> + false; +extract_deadline(Event, DefaultT, StopTSupported, TRef, MappingVal) -> + extract_deadline(Event, DefaultT, StopTSupported, TRef, MappingVal, now()). + +extract_deadline(_, _, _, _, false, _) -> + false; +extract_deadline(#'CosNotification_StructuredEvent' + {header = #'CosNotification_EventHeader' + {variable_header = VH}}, DefaultT, StopTSupported, + TRef, undefined, Now) -> + DL = case extract_value(VH, ?not_Timeout, undefined) of + undefined when StopTSupported == true, TRef =/= undefined -> + case extract_value(VH, ?not_StopTime, undefined) of + undefined -> + DefaultT; + DefinedTime -> + DefinedTime + end; + undefined -> + DefaultT; + DefinedTime -> + DefinedTime + end, + convert_time(DL, TRef, Now); +%% Maybe a unstructured event. +extract_deadline(_, Time, _, TRef, undefined, Now) -> + convert_time(Time, TRef, Now); +extract_deadline(_, _, _, TRef, DOverride, Now) -> + %% Must have an associated MappingFilter defining a Deadline. + convert_time(DOverride, TRef, Now). + +convert_time(0, _, _) -> + false; +convert_time(UTC, TRef, {M,S,U}) when is_record(UTC, 'TimeBase_UtcT') -> + case catch get_time_diff(UTC, TRef) of + {'EXCEPTION', _} -> + false; + {'EXIT', _} -> + false; + DL -> + MicroSecs = round(DL/10), + Secs = round(MicroSecs/1000000), + MegaSecs = round(Secs/1000000), + {-M-MegaSecs, -S-Secs+MegaSecs, -U-MicroSecs+Secs} + end; +convert_time(DL, _, {M,S,U}) when is_integer(DL) -> + MicroSecs = round(DL/10), + Secs = round(MicroSecs/1000000), + MegaSecs = round(Secs/1000000), + {-M-MegaSecs, -S-Secs+MegaSecs, -U-MicroSecs+Secs}; +convert_time(_, _, _) -> + false. + + +get_time_diff(UTC, TRef) -> + UTO = 'CosTime_TimeService':universal_time(TRef), + UTO2 = 'CosTime_TimeService':uto_from_utc(TRef, UTC), + TIO = 'CosTime_UTO':time_to_interval(UTO, UTO2), + #'TimeBase_IntervalT'{lower_bound=LB, upper_bound = UB} = + 'CosTime_TIO':'_get_time_interval'(TIO), + UB-LB. + +check_deadline(DL) when is_tuple(DL) -> + {M,S,U} = now(), + DL >= {-M,-S,-U}; +check_deadline(_DL) -> + %% This case will cover if no timeout is set. + false. + +check_start_time(ST) when is_tuple(ST) -> + {M,S,U} = now(), + ST >= {-M,-S,-U}; +check_start_time(_ST) -> + %% This case will cover if no earliest delivery time is set. + true. + +%%------------------------------------------------------------ +%% function : extract_value +%% Arguments: A Property Sequence +%% ID - wanted property string() +%% Other - default-value. +%% Returns : Value associated with given ID or default value. +%%------------------------------------------------------------ +extract_value([], _, Other) -> + Other; +extract_value([#'CosNotification_Property'{name=ID, value=V}|_], ID, _) -> + any:get_value(V); +extract_value([_H|T], ID, Other) -> + extract_value(T, ID, Other). + +%%------------------------------------------------------------ +%% function : get_event +%% Arguments: +%% Returns : +%%------------------------------------------------------------ +get_event(DBRef) -> + get_event(DBRef, true). +get_event(DBRef, Delete) -> + case get_events(DBRef, 1, Delete) of + {[], false} -> + {[], false}; + {[], false, Keys} -> + {[], false, Keys}; + {[Event], Bool} -> + {Event, Bool}; + {[Event], Bool, Keys} -> + {Event, Bool, Keys} + end. + +%%------------------------------------------------------------ +%% function : get_events +%% Arguments: +%% Returns : A list of events (possibly empty) and a boolean +%% indicating if event found. +%% Comments : Try to extract Max events from the database. +%%------------------------------------------------------------ +get_events(#dbRef{orderRef = ORef, discardRef = DRef}, Max) -> + event_loop(ets:last(ORef), ORef, DRef, Max, [], [], true). + +get_events(#dbRef{orderRef = ORef, discardRef = DRef}, Max, Delete) -> + event_loop(ets:last(ORef), ORef, DRef, Max, [], [], Delete). + +event_loop('$end_of_table', _, _, _, [], _, true) -> + {[], false}; +event_loop('$end_of_table', _, _, _, [], [], _) -> + {[], false, []}; +event_loop('$end_of_table', _ORef, _, _, Accum, _Keys, true) -> + {lists:reverse(Accum), true}; +event_loop('$end_of_table', _ORef, _, _, Accum, Keys, _) -> + {lists:reverse(Accum), true, Keys}; +event_loop(_, _ORef, _, 0, [], _Keys, true) -> + %% Only possible if some tries to pull a sequence of 0 events. + %% Should we really test for this case? + {[], false}; +event_loop(_, _ORef, _, 0, [], Keys, _) -> + {[], false, Keys}; +event_loop(_, _ORef, _, 0, Accum, _Keys, true) -> + {lists:reverse(Accum), true}; +event_loop(_, _ORef, _, 0, Accum, Keys, _) -> + {lists:reverse(Accum), true, Keys}; +event_loop(Key, ORef, undefined, Left, Accum, Keys, Delete) -> + [{_,DL,ST,_PO,Event}]=ets:lookup(ORef, Key), + case check_deadline(DL) of + true -> + ets:delete(ORef, Key), + event_loop(ets:prev(ORef, Key), ORef, undefined, + Left, Accum, Keys, Delete); + false -> + case check_start_time(ST) of + true when Delete == true -> + ets:delete(ORef, Key), + event_loop(ets:prev(ORef, Key), ORef, undefined, + Left-1, [Event|Accum], Keys, Delete); + true -> + event_loop(ets:prev(ORef, Key), ORef, undefined, + Left-1, [Event|Accum], [{ORef, Key}|Keys], Delete); + false -> + event_loop(ets:prev(ORef, Key), ORef, undefined, + Left, Accum, Keys, Delete) + end + end; +event_loop({Key1, Key2}, ORef, DRef, Left, Accum, Keys, Delete) -> + [{_,DL,ST,_PO,Event}]=ets:lookup(ORef, {Key1, Key2}), + case check_deadline(DL) of + true -> + ets:delete(ORef, {Key1, Key2}), + ets:delete(DRef, {Key2, Key1}), + event_loop(ets:prev(ORef, {Key1, Key2}), ORef, DRef, + Left, Accum, Keys, Delete); + false -> + case check_start_time(ST) of + true when Delete == true -> + ets:delete(ORef, {Key1, Key2}), + ets:delete(DRef, {Key2, Key1}), + event_loop(ets:prev(ORef, {Key1, Key2}), ORef, DRef, + Left-1, [Event|Accum], Keys, Delete); + true -> + event_loop(ets:prev(ORef, {Key1, Key2}), ORef, DRef, + Left-1, [Event|Accum], + [{ORef, {Key1, Key2}}, {DRef, {Key2, Key1}}|Keys], + Delete); + false -> + event_loop(ets:prev(ORef, {Key1, Key2}), ORef, DRef, + Left, Accum, Keys, Delete) + end + end; +event_loop({Key1, Key2, Key3}, ORef, DRef, Left, Accum, Keys, Delete) -> + [{_,DL,ST,_PO,Event}]=ets:lookup(ORef, {Key1, Key2, Key3}), + case check_deadline(DL) of + true -> + ets:delete(ORef, {Key1, Key2, Key3}), + ets:delete(DRef, {Key3, Key2, Key1}), + event_loop(ets:prev(ORef, {Key1, Key2, Key3}), ORef, DRef, + Left, Accum, Keys, Delete); + false -> + case check_start_time(ST) of + true when Delete == true -> + ets:delete(ORef, {Key1, Key2, Key3}), + ets:delete(DRef, {Key3, Key2, Key1}), + event_loop(ets:prev(ORef, {Key1, Key2, Key3}), ORef, DRef, + Left-1, [Event|Accum], Keys, Delete); + true -> + event_loop(ets:prev(ORef, {Key1, Key2, Key3}), ORef, DRef, + Left-1, [Event|Accum], + [{ORef, {Key1, Key2, Key3}}, + {DRef, {Key3, Key2, Key1}}|Keys], Delete); + false -> + event_loop(ets:prev(ORef, {Key1, Key2, Key3}), ORef, DRef, + Left, Accum, Keys, Delete) + end + end. + +%%------------------------------------------------------------ +%% function : delete_events +%% Arguments: EventList - what's returned by get_event, get_events +%% and add_and_get_event. +%% Returns : +%% Comment : Shall be invoked when it's safe to premanently remove +%% the events found in the EventList. +%% +%%------------------------------------------------------------ +delete_events([]) -> + ok; +delete_events([{DB, Key}|T]) -> + ets:delete(DB, Key), + delete_events(T). + +%%------------------------------------------------------------ +%% function : update +%% Arguments: +%% Returns : +%% Comment : As default we shall deliver Events in Priority order. +%% Hence, if AnyOrder set we will still deliver in +%% Priority order. +%%------------------------------------------------------------ +update(undefined, _QoS) -> + ok; +update(DBRef, QoS) -> + update(DBRef, QoS, undefined, undefined). + +update(DBRef, QoS, LifeFilter, PrioFilter) -> + case updated_order(DBRef, ?not_GetOrderPolicy(QoS)) of + false -> + case updated_discard(DBRef, ?not_GetDiscardPolicy(QoS)) of + false -> + DBR2 = ?set_DefPriority(DBRef, ?not_GetPriority(QoS)), + DBR3 = ?set_MaxEvents(DBR2, ?not_GetMaxEventsPerConsumer(QoS)), + DBR4 = ?set_DefStopT(DBR3, ?not_GetTimeout(QoS)), + DBR5 = ?set_StartTsupport(DBR4, ?not_GetStartTimeSupported(QoS)), + DBR6 = ?set_StopTsupport(DBR5, ?not_GetStopTimeSupported(QoS)), + case ets:info(?get_OrderRef(DBR6), size) of + N when N =< ?get_MaxEvents(DBR6) -> + %% Even if the QoS MaxEvents have been changed + %% we don't reach the limit. + DBR6; + N -> + %% The QoS MaxEvents must have been decreased. + discard_events(DBR6, N-?get_MaxEvents(DBR6)), + DBR6 + end; + true -> + destroy_discard_db(DBRef), + NewDBRef = create_db(QoS, ?get_GCTime(DBRef), ?get_GCLimit(DBRef), + ?get_TimeRef(DBRef)), + move_events(DBRef, NewDBRef, ets:first(?get_OrderRef(DBRef)), + LifeFilter, PrioFilter) + end; + true -> + destroy_discard_db(DBRef), + NewDBRef = create_db(QoS, ?get_GCTime(DBRef), ?get_GCLimit(DBRef), + ?get_TimeRef(DBRef)), + move_events(DBRef, NewDBRef, ets:first(?get_OrderRef(DBRef)), + LifeFilter, PrioFilter) + end. + +updated_order(#dbRef{orderPolicy = Equal}, Equal) -> false; +updated_order(#dbRef{orderPolicy = ?not_PriorityOrder}, ?not_AnyOrder) -> false; +updated_order(#dbRef{orderPolicy = ?not_AnyOrder}, ?not_PriorityOrder) -> false; +updated_order(_, _) -> true. + +updated_discard(#dbRef{discardPolicy = Equal}, Equal) -> false; +updated_discard(#dbRef{discardPolicy = ?not_RejectNewEvents}, ?not_AnyOrder) -> false; +updated_discard(#dbRef{discardPolicy = ?not_AnyOrder}, ?not_RejectNewEvents) -> false; +updated_discard(_, _) -> true. + +move_events(DBRef, NewDBRef, '$end_of_table', _, _) -> + destroy_order_db(DBRef), + case ets:info(?get_OrderRef(NewDBRef), size) of + N when N =< ?get_MaxEvents(NewDBRef) -> + %% Even if the QoS MaxEvents have been changed + %% we don't reach the limit. + NewDBRef; + N -> + %% The QoS MaxEvents must have been decreased. + discard_events(DBRef, N-?get_MaxEvents(NewDBRef)), + NewDBRef + end; +move_events(DBRef, NewDBRef, Key, LifeFilter, PrioFilter) -> + [{Keys, DeadLine, StartTime, PriorityOverride, Event}] = + ets:lookup(?get_OrderRef(DBRef), Key), + case check_deadline(DeadLine) of + true -> + ok; + _-> + write_event(?get_OrderP(DBRef), + {Keys, DeadLine, StartTime, PriorityOverride, Event}, + DBRef, NewDBRef, Key, LifeFilter, PrioFilter) + end, + ets:delete(?get_OrderRef(DBRef), Key), + move_events(DBRef, NewDBRef, ets:next(?get_OrderRef(DBRef), Key), + LifeFilter, PrioFilter). + +%% We cannot use do_add_event directly since we MUST lookup the timestamp (TS). +write_event(?not_DeadlineOrder, {{_, TS, _Prio}, DL, ST, PO, Event}, _DBRef, NewDBRef, + _Key, _LifeFilter, _PrioFilter) -> + StartT = update_starttime(NewDBRef, Event, ST), + %% Deadline and Priority previously extracted. + do_add_event(NewDBRef, Event, TS, DL, StartT, PO); +write_event(?not_DeadlineOrder, {{_, TS}, DL, _ST, PO, Event}, _DBRef, NewDBRef, + _Key, _LifeFilter, PrioFilter) -> + %% Priority not previously extracted. + POverride = update_priority(NewDBRef, PrioFilter, Event, PO), + StartT = extract_start_time(Event, ?get_StartTsupport(NewDBRef), + ?get_TimeRef(NewDBRef)), + do_add_event(NewDBRef, Event, TS, DL, StartT, POverride); +write_event(?not_FifoOrder, {{TS, _PorD}, DL, ST, PO, Event}, _DBRef, NewDBRef, + _Key, LifeFilter, PrioFilter) -> + %% Priority or Deadline have been extracted before but we cannot tell which. + POverride = update_priority(NewDBRef, PrioFilter, Event, PO), + DeadL = update_deadline(NewDBRef, LifeFilter, Event, TS, DL), + StartT = update_starttime(NewDBRef, Event, ST), + do_add_event(NewDBRef, Event, TS, DeadL, StartT, POverride); +write_event(?not_FifoOrder, {TS, DL, ST, PO, Event}, _DBRef, NewDBRef, + _Key, LifeFilter, PrioFilter) -> + %% Priority and Deadline not extracetd before. Do it now. + POverride = update_priority(NewDBRef, PrioFilter, Event, PO), + DeadL = update_deadline(NewDBRef, LifeFilter, Event, TS, DL), + StartT = update_starttime(NewDBRef, Event, ST), + do_add_event(NewDBRef, Event, TS, DeadL, StartT, POverride); +%% Order Policy must be AnyOrder or PriorityOrder. +write_event(_, {{_Prio, TS}, DL, ST, PO, Event}, _DBRef, NewDBRef, + _Key, LifeFilter, _PrioFilter) -> + DeadL = update_deadline(NewDBRef, LifeFilter, Event, TS, DL), + StartT = update_starttime(NewDBRef, Event, ST), + do_add_event(NewDBRef, Event, TS, DeadL, StartT, PO); +write_event(_, {{_Prio, TS, DL}, DL, ST, PO, Event}, _DBRef, NewDBRef, _Key, _, _) -> + %% Both Deadline and Priority have been extracetd before. + StartT = update_starttime(NewDBRef, Event, ST), + do_add_event(NewDBRef, Event, TS, DL, StartT, PO). + + +%%------------------------------------------------------------ +%% function : update_priority +%% Arguments: +%% Returns : +%% Comment : The purpose with this function is to avoid +%% calling MappingFilter priority again, especially +%% deadline again (we especially want to avoid calling +%% since it may require intra-ORB communication. +%% Use only when doing an update. +%%------------------------------------------------------------ +update_priority(DBRef, PrioFilter, Event, OldPrio) when is_atom(OldPrio) -> + get_prio_mapping_value(DBRef, PrioFilter, Event); +update_priority(_DBRef, _PrioFilter, _Event, OldPrio) -> + OldPrio. + +%%------------------------------------------------------------ +%% function : update_deadline +%% Arguments: +%% Returns : +%% Comment : The purpose with this function is to avoid +%% calling MappingFilter or parsing the events for +%% deadline again (we especially want to avoid calling +%% the MappingFilter since it may require intra-ORB +%% communication. Use only when doing an update. +%%------------------------------------------------------------ +update_deadline(DBRef, _LifeFilter, _Event, _TS, _OldDeadL) when + ?get_DiscardP(DBRef) =/= ?not_DeadlineOrder, + ?get_OrderP(DBRef) =/= ?not_DeadlineOrder, + ?is_StopTNotSupported(DBRef) -> + %% We do not need to extract the Deadline since it will not be used. + false; +update_deadline(DBRef, LifeFilter, Event, TS, OldDeadL) when is_atom(OldDeadL) -> + %% We need the Deadline and it have not been extracetd before. + DOverride = get_life_mapping_value(DBRef, LifeFilter, Event), + %% We must find out when the event was delivered; setting a deadline using + %% a new timestamp would not be accurate since we cannot tell for how long + %% the event have been waiting. + OldNow = convert_FIFO_Key(TS), + extract_deadline(Event, ?get_DefStopT(DBRef), ?get_StopTsupport(DBRef), + ?get_TimeRef(DBRef), DOverride, OldNow); +update_deadline(_DBRef, _LifeFilter, _Event, _TS, OldDeadL) -> + %% We need the Deadline and it have been extracetd before. + OldDeadL. + +%%------------------------------------------------------------ +%% function : update_starttime +%% Arguments: +%% Returns : +%% Comment : The purpose with this function is to avoid +%% parsing the events for starttime again. +%% Use only when doing an update. +%%------------------------------------------------------------ +update_starttime(DBRef, Event, OldStartT) when is_atom(OldStartT) -> + %% Probably not previously extracted; try to get it. + extract_start_time(Event, ?get_StartTsupport(DBRef), ?get_TimeRef(DBRef)); +update_starttime(_DBRef, _Event, OldStartT) -> + %% Previously extracted. + OldStartT. + +%%------------------------------------------------------------ +%% function : discard_events +%% Arguments: DBRef +%% N - number of events we must discard. +%% Returns : +%% Comment : As default we shall Reject New Events when the limit +%% is reached. Any discard order will do the same. +%% +%% This function can only be used for the discard policies +%% Fifo, Priority and Deadline. Any or RejectNewEvents +%% will not allow events to be stored at all, i.e., no events +%% to discard. Lifo will not be stored either since when +%% trying to add an event it is definitely the last event in. +%%------------------------------------------------------------ +%% Since no Discard DB must the same Order policy. +discard_events(#dbRef{orderRef = ORef, discardRef = undefined, + discardPolicy = ?not_DeadlineOrder}, N) -> + ?debug_print("Discarding ~p events Deadline Order.",[N]), + index_loop_backward(ets:last(ORef), undefined, ORef, N); +discard_events(#dbRef{orderRef = ORef, discardRef = DRef, + discardPolicy = ?not_DeadlineOrder}, N) -> + ?debug_print("Discarding ~p events Deadline Order.",[N]), + index_loop_backward(ets:last(DRef), DRef, ORef, N); +%% Fifo. +discard_events(#dbRef{orderRef = ORef, discardRef = undefined, + discardPolicy = ?not_FifoOrder}, N) -> + ?debug_print("Discarding ~p events Fifo Order.",[N]), + index_loop_backward(ets:last(ORef), undefined, ORef, N); +discard_events(#dbRef{orderRef = ORef, discardRef = DRef, + discardPolicy = ?not_FifoOrder}, N) -> + ?debug_print("Discarding ~p events Fifo Order.",[N]), + index_loop_backward(ets:last(DRef), DRef, ORef, N); +%% Lifo- or Priority-Order +discard_events(#dbRef{orderRef = ORef, discardRef = undefined}, N) -> + ?debug_print("Discarding ~p events Lifo- or Priority-Order.",[N]), + index_loop_forward(ets:first(ORef), undefined, ORef, N); +discard_events(#dbRef{orderRef = ORef, discardRef = DRef}, N) -> + ?debug_print("Discarding ~p events Lifo- or Priority-Order.",[N]), + index_loop_forward(ets:first(DRef), DRef, ORef, N). + + +index_loop_forward('$end_of_table', _, _, _Left) -> + ok; +index_loop_forward(_, _, _, 0) -> + ok; +index_loop_forward(Key, undefined, ORef, Left) -> + ets:delete(ORef, Key), + NewKey=ets:next(ORef, Key), + index_loop_forward(NewKey, undefined, ORef, Left-1); + +index_loop_forward({Key1, Key2, Key3}, DRef, ORef, Left) -> + ets:delete(DRef, {Key1, Key2, Key3}), + ets:delete(ORef, {Key3, Key2, Key1}), + NewKey=ets:next(DRef, {Key1, Key2, Key3}), + index_loop_forward(NewKey, DRef, ORef, Left-1); + +index_loop_forward({Key1, Key2}, DRef, ORef, Left) -> + ets:delete(DRef, {Key1, Key2}), + ets:delete(ORef, {Key2, Key1}), + NewKey=ets:next(DRef, {Key1, Key2}), + index_loop_forward(NewKey, DRef, ORef, Left-1). + +index_loop_backward('$end_of_table', _, _, _) -> + ok; +index_loop_backward(_, _, _, 0) -> + ok; +index_loop_backward(Key, undefined, ORef, Left) -> + ets:delete(ORef, Key), + NewKey=ets:prev(ORef, Key), + index_loop_backward(NewKey, undefined, ORef, Left-1); +index_loop_backward({Key1, Key2}, DRef, ORef, Left) -> + ets:delete(DRef, {Key1, Key2}), + ets:delete(ORef, {Key2, Key1}), + NewKey=ets:prev(DRef, {Key1, Key2}), + index_loop_backward(NewKey, DRef, ORef, Left-1); +index_loop_backward({Key1, Key2, Key3}, DRef, ORef, Left) -> + ets:delete(DRef, {Key1, Key2, Key3}), + ets:delete(ORef, {Key3, Key2, Key1}), + NewKey=ets:prev(DRef, {Key1, Key2, Key3}), + index_loop_backward(NewKey, DRef, ORef, Left-1). + +%%------------------------------------------------------------ +%% function : add_and_get_event +%% Arguments: DBRef and Event +%% Returns : {[], bool()} | {Event, bool()} +%% Comment : This function is a mixture of ad anf get events. +%% The intended use to avoid storing an event when +%% not necessary. +%%------------------------------------------------------------ +add_and_get_event(DBRef, Event) -> + add_and_get_event(DBRef, Event, undefined, undefined, true). + +add_and_get_event(DBRef, Event, Delete) -> + add_and_get_event(DBRef, Event, undefined, undefined, Delete). + +add_and_get_event(DBRef, Event, LifeFilter, PrioFilter) -> + add_and_get_event(DBRef, Event, LifeFilter, PrioFilter, true). + +add_and_get_event(DBRef, Event, LifeFilter, PrioFilter, Delete) -> + case ets:info(?get_OrderRef(DBRef), size) of + 0 when ?is_StartTNotSupported(DBRef), ?is_StopTNotSupported(DBRef), + Delete == true -> + %% No stored events and no timeouts used; just return the event. + {Event, false}; + 0 when ?is_StartTNotSupported(DBRef), ?is_StopTNotSupported(DBRef) -> + %% No stored events and no timeouts used; just return the event. + {Event, false, []}; + 0 when ?is_StartTNotSupported(DBRef) -> + %% Only deadline supported, lookup values and cehck if ok. + DOverride = get_life_mapping_value(DBRef, LifeFilter, Event), + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), ?get_TimeRef(DBRef), + DOverride), + case check_deadline(DL) of + true when Delete == true -> + %% Expired, just discard the event. + {[], false}; + true -> + {[], false, []}; + _ when Delete == true -> + %% Not expired, we can safely return the event. + {Event, false}; + _ -> + %% Not expired, we can safely return the event. + {Event, false, []} + end; + 0 when ?is_StopTNotSupported(DBRef) -> + %% Only starttime allowed, test if we can deliver the event now. + ST = extract_start_time(Event, ?get_StartTsupport(DBRef), + ?get_TimeRef(DBRef)), + case check_start_time(ST) of + false when Delete == true -> + DOverride = get_life_mapping_value(DBRef, LifeFilter, Event), + POverride = get_prio_mapping_value(DBRef, PrioFilter, Event), + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), + ?get_TimeRef(DBRef), DOverride), + do_add_event(DBRef, Event, create_FIFO_Key(), DL, ST, POverride), + {[], true}; + false -> + DOverride = get_life_mapping_value(DBRef, LifeFilter, Event), + POverride = get_prio_mapping_value(DBRef, PrioFilter, Event), + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), + ?get_TimeRef(DBRef), DOverride), + do_add_event(DBRef, Event, create_FIFO_Key(), DL, ST, POverride), + {[], true, []}; + _ when Delete == true -> + %% Starttime ok, just return the event. + {Event, false}; + _ -> + %% Starttime ok, just return the event. + {Event, false, []} + end; + _-> + %% Event already stored, just have to accept the overhead. + ST = extract_start_time(Event, ?get_StartTsupport(DBRef), + ?get_TimeRef(DBRef)), + DOverride = get_life_mapping_value(DBRef, LifeFilter, Event), + POverride = get_prio_mapping_value(DBRef, PrioFilter, Event), + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), + ?get_TimeRef(DBRef), DOverride), + do_add_event(DBRef, Event, create_FIFO_Key(), DL, ST, POverride), + get_event(DBRef, Delete) + end. + +%%------------------------------------------------------------ +%% function : add_event +%% Arguments: DBRef and Event +%% Returns : true (or whatever 'ets:insert' returns) | +%% {'EXCEPTION', #'IMP_LIMIT'{}} +%% Comment : As default we shall deliver Events in Priority order. +%% Hence, if AnyOrder set we will still deliver in +%% Priority order. But we cannot use only the Priority +%% value since if "all" events have the same priority +%% there is a risk that some never will be delivered if +%% the EventDB always contain events. +%% +%% When discard and order policy is equal we only use one +%% DB since all we have to do is to "read from the other +%% end" to discard the correct event(s). +%% +%% In the discard DB we must also store keys necessary to +%% lookup the event in the order DB. +%% +%% If event limit reached 'IMPL_LIMIT' is raised if +%% the discard policy is RejectNewEvents or AnyOrder. +%% Theses two policies we currently define to be equal. +%%------------------------------------------------------------ + +add_event(DBRef, Event) -> + %% Save overhead by first checking if we really need to extract + %% Deadline and/or Priority. + Deadline = get_life_mapping_value(DBRef, undefined, Event), + Priority = get_prio_mapping_value(DBRef, undefined, Event), + add_event_helper(DBRef, Event, Deadline, Priority). + +add_event(DBRef, Event, LifeFilter, PrioFilter) -> + %% Save overhead by first checking if we really need to extract + %% Deadline and/or Priority. + Deadline = get_life_mapping_value(DBRef, LifeFilter, Event), + Priority = get_prio_mapping_value(DBRef, PrioFilter, Event), + add_event_helper(DBRef, Event, Deadline, Priority). + +add_event_helper(DBRef, Event, DOverride, POverride) -> + case ets:info(?get_OrderRef(DBRef), size) of + N when N < ?get_MaxEvents(DBRef), N > ?get_GCLimit(DBRef) -> + gc_events(DBRef, low), + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), ?get_TimeRef(DBRef), + DOverride), + case check_deadline(DL) of + true -> + true; + _ -> + ST = extract_start_time(Event, ?get_StartTsupport(DBRef), + ?get_TimeRef(DBRef)), + do_add_event(DBRef, Event, create_FIFO_Key(), DL, ST, POverride) + end; + N when N < ?get_MaxEvents(DBRef) -> + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), ?get_TimeRef(DBRef), + DOverride), + case check_deadline(DL) of + true -> + true; + _ -> + ST = extract_start_time(Event, ?get_StartTsupport(DBRef), + ?get_TimeRef(DBRef)), + do_add_event(DBRef, Event, create_FIFO_Key(), DL, ST, POverride) + end; + _N when ?get_DiscardP(DBRef) == ?not_RejectNewEvents -> + gc_events(DBRef, low), + corba:raise(#'IMP_LIMIT'{completion_status=?COMPLETED_NO}); + _N when ?get_DiscardP(DBRef) == ?not_AnyOrder -> + gc_events(DBRef, low), + corba:raise(#'IMP_LIMIT'{completion_status=?COMPLETED_NO}); + _N when ?get_DiscardP(DBRef) == ?not_LifoOrder -> + gc_events(DBRef, low), + corba:raise(#'IMP_LIMIT'{completion_status=?COMPLETED_NO}); + _N -> + gc_events(DBRef, low), + %% Other discard policy; we must first store the event + %% and the look up in the Discard DB which event we + %% should remove. + DL = extract_deadline(Event, ?get_DefStopT(DBRef), + ?get_StopTsupport(DBRef), ?get_TimeRef(DBRef), + DOverride), + case check_deadline(DL) of + true -> + true; + _ -> + ST = extract_start_time(Event, ?get_StartTsupport(DBRef), + ?get_TimeRef(DBRef)), + do_add_event(DBRef, Event, create_FIFO_Key(), DL, ST, POverride), + discard_events(DBRef, 1) + end + end. + + +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_DeadlineOrder, + discardRef = DRef, discardPolicy = ?not_PriorityOrder, + defPriority = DefPrio, defStopT = _DefStopT}, Event, Key, DL, ST, PO) -> + Prio = extract_priority(Event, DefPrio, PO), + ets:insert(ORef, {{DL, Key, Prio}, DL, ST, PO, Event}), + ets:insert(DRef, {{Prio, Key, DL}}); +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_DeadlineOrder, + discardRef = DRef, discardPolicy = ?not_FifoOrder, + defStopT = _DefStopT}, Event, Key, DL, ST, PO) -> + ets:insert(ORef, {{DL, Key}, DL, ST, PO, Event}), + ets:insert(DRef, {{Key, DL}}); +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_DeadlineOrder, + discardRef = DRef, discardPolicy = ?not_LifoOrder, + defStopT = _DefStopT}, Event, Key, DL, ST, PO) -> + ets:insert(ORef, {{DL, Key}, DL, ST, PO, Event}), + ets:insert(DRef, {{Key, DL}}); +%% Either the same (DeadlineOrder), RejectNewEvents or AnyOrder. No need +%% to store anything in the discard policy, i.e., if the same we'll just +%% read "from the other end" and AnyOrder and RejectNewEvents is equal. +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_DeadlineOrder, + defStopT = _DefStopT}, Event, Key, DL, ST, PO) -> + ets:insert(ORef, {{DL, Key}, DL, ST, PO, Event}); + + +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_FifoOrder, + discardRef = DRef, discardPolicy = ?not_DeadlineOrder, + defStopT = _DefStopT}, Event, Key, DL, ST, PO) -> + ets:insert(ORef, {{Key, DL}, DL, ST, PO, Event}), + ets:insert(DRef, {{DL, Key}}); +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_FifoOrder, + discardRef = DRef, discardPolicy = ?not_PriorityOrder, + defPriority = DefPrio}, Event, Key, DL, ST, PO) -> + Prio = extract_priority(Event, DefPrio, PO), + ets:insert(ORef, {{Key, Prio}, DL, ST, PO, Event}), + ets:insert(DRef, {{Prio, Key}}); +%% The discard policy must RejectNewEvents, AnyOrder, Fifo or Lifo order. +do_add_event(#dbRef{orderRef = ORef, orderPolicy = ?not_FifoOrder, + discardRef = _DRef}, Event, Key, DL, ST, PO) -> + ets:insert(ORef, {Key, DL, ST, PO, Event}); + +%% Order Policy must be AnyOrder or PriorityOrder. +do_add_event(#dbRef{orderRef = ORef, + discardRef = DRef, discardPolicy = ?not_DeadlineOrder, + defPriority = DefPrio, defStopT = _DefStopT}, Event, Key, DL, ST, PO) -> + Prio = extract_priority(Event, DefPrio, PO), + ets:insert(ORef, {{Prio, Key, DL}, DL, ST, PO, Event}), + ets:insert(DRef, {{DL, Key, Prio}}); +do_add_event(#dbRef{orderRef = ORef, + discardRef = DRef, discardPolicy = ?not_FifoOrder, + defPriority = DefPrio}, Event, Key, DL, ST, PO) -> + Prio = extract_priority(Event, DefPrio, PO), + ets:insert(ORef, {{Prio, Key}, DL, ST, PO, Event}), + ets:insert(DRef, {{Key, Prio}}); + +do_add_event(#dbRef{orderRef = ORef, + discardRef = DRef, discardPolicy = ?not_LifoOrder, + defPriority = DefPrio}, Event, Key, DL, ST, PO) -> + Prio = extract_priority(Event, DefPrio, PO), + ets:insert(ORef, {{Prio, Key}, DL, ST, PO, Event}), + ets:insert(DRef, {{Key, Prio}}); + +%% Order Policy must be AnyOrder or PriorityOrder and Discard Policy must be +%% AnyOrder or RejectNewEvents +do_add_event(#dbRef{orderRef = ORef, defPriority = DefPrio}, Event, Key, DL, ST, PO) -> + Prio = extract_priority(Event, DefPrio, PO), + ets:insert(ORef, {{Prio, Key}, DL, ST, PO, Event}). + +%%------------------------------------------------------------ +%% function : destroy_db +%% Arguments: A DB reference +%% Returns : +%%------------------------------------------------------------ +destroy_db(#dbRef{orderRef = ORef, discardRef = undefined}) -> + ets:delete(ORef); +destroy_db(#dbRef{orderRef = ORef, discardRef = DRef}) -> + ets:delete(ORef), + ets:delete(DRef). + +%%------------------------------------------------------------ +%% function : destroy_discard_db +%% Arguments: A DB reference +%% Returns : +%%------------------------------------------------------------ +destroy_discard_db(#dbRef{discardRef = undefined}) -> + ok; +destroy_discard_db(#dbRef{discardRef = DRef}) -> + ets:delete(DRef). + +%%------------------------------------------------------------ +%% function : destroy_order_db +%% Arguments: A DB reference +%% Returns : +%%------------------------------------------------------------ +destroy_order_db(#dbRef{orderRef = ORef}) -> + ets:delete(ORef). + +%%------------------------------------------------------------ +%% function : create_db +%% Arguments: QoS (local representation). +%% Returns : A DB reference +%%------------------------------------------------------------ +create_db(QoS, GCTime, GCLimit, TimeRef) -> + DiscardRef = + case {?not_GetDiscardPolicy(QoS), ?not_GetOrderPolicy(QoS)} of + {Equal, Equal} -> + undefined; + {?not_PriorityOrder, ?not_AnyOrder} -> + %% NOTE: Any- and Priority-Order delivery policy is equal. + undefined; + {?not_RejectNewEvents, _} -> + undefined; + {?not_AnyOrder, _} -> + undefined; + {?not_LifoOrder, ?not_FifoOrder} -> + undefined; + _ -> + ets:new(oe_ets, [set, public, ordered_set]) + end, + DBRef = ?CreateRef(ets:new(oe_ets, [set, public, ordered_set]), + DiscardRef, + ?not_GetOrderPolicy(QoS), ?not_GetDiscardPolicy(QoS), + ?not_GetPriority(QoS), ?not_GetMaxEventsPerConsumer(QoS), + ?not_GetTimeout(QoS), ?not_GetStartTimeSupported(QoS), + ?not_GetStopTimeSupported(QoS), GCTime, GCLimit, TimeRef), + if + ?is_TimeoutNotUsed(DBRef), ?is_StopTNotSupported(DBRef) -> + ok; + true -> + {M,S,U} = now(), + put(oe_GC_timestamp, {M,S+GCTime,U}) + end, + DBRef. + +%%------------------------------------------------------------ +%% function : get_prio_mapping_value +%% Arguments: A MappingFilter reference | undefined +%% Event (Any or Structured) +%% Returns : undefined | Data +%%------------------------------------------------------------ +get_prio_mapping_value(DBRef, _, _) when ?get_DiscardP(DBRef) =/= ?not_PriorityOrder, + ?get_OrderP(DBRef) =/= ?not_AnyOrder, + ?get_OrderP(DBRef) =/= ?not_PriorityOrder -> + false; +get_prio_mapping_value(_, undefined, _) -> + undefined; +get_prio_mapping_value(_, MFilter, Event) when is_record(Event, 'any') -> + case catch 'CosNotifyFilter_MappingFilter':match(MFilter, Event) of + {false, DefVal} when is_record(DefVal, 'any') -> + any:get_value(DefVal); + {true, Matched} when is_record(Matched, 'any') -> + any:get_value(Matched); + _ -> + undefined + end; +get_prio_mapping_value(_, MFilter, Event) -> + case catch 'CosNotifyFilter_MappingFilter':match_structured(MFilter, Event) of + {false, DefVal} when is_record(DefVal, 'any') -> + any:get_value(DefVal); + {true, Matched} when is_record(Matched, 'any') -> + any:get_value(Matched); + _ -> + undefined + end. + +%%------------------------------------------------------------ +%% function : get_life_mapping_value +%% Arguments: A MappingFilter reference | undefined +%% Event (Any or Structured) +%% Returns : undefined | Data +%%------------------------------------------------------------ +get_life_mapping_value(DBRef, _, _) when ?get_DiscardP(DBRef) =/= ?not_DeadlineOrder, + ?get_OrderP(DBRef) =/= ?not_DeadlineOrder, + ?is_StopTNotSupported(DBRef) -> + false; +get_life_mapping_value(_, undefined, _) -> + undefined; +get_life_mapping_value(_, MFilter, Event) when is_record(Event, 'any') -> + case catch 'CosNotifyFilter_MappingFilter':match(MFilter, Event) of + {false, DefVal} when is_record(DefVal, 'any') -> + any:get_value(DefVal); + {true, Matched} when is_record(Matched, 'any') -> + any:get_value(Matched); + _ -> + undefined + end; +get_life_mapping_value(_, MFilter, Event) -> + case catch 'CosNotifyFilter_MappingFilter':match_structured(MFilter, Event) of + {false, DefVal} when is_record(DefVal, 'any') -> + any:get_value(DefVal); + {true, Matched} when is_record(Matched, 'any') -> + any:get_value(Matched); + _ -> + undefined + end. + +%%------------------------------------------------------------ +%% function : validate_event +%% Arguments: Subscribe data +%% A sequence of Events, 'structured' or an 'any' record +%% A list of filter references +%% Status, i.e., do we have to filter the events or just check subscr. +%% Returns : A tuple of two lists; list1 the events that passed +%% and list2 the events that didn't pass. +%%------------------------------------------------------------ +validate_event(true, Events, Filters, _, 'MATCH') -> + filter_events(Events, Filters, false); +validate_event(true, Events, _Filters, _, _) -> + {Events, []}; +validate_event({_Which, _WC}, Event, Filters, _, 'MATCH') when is_record(Event, any) -> + filter_events(Event, Filters, false); +validate_event({_Which, _WC}, Event, _Filters, _, _) when is_record(Event, any) -> + {Event, []}; +validate_event({Which, WC}, Events, Filters, DBRef, 'MATCH') -> + Passed=validate_event2(DBRef, Events, Which, WC, []), + filter_events(Passed, Filters, true); +validate_event({Which, WC}, Events, _Filters, DBRef, _) -> + Passed=validate_event2(DBRef, Events, Which, WC, []), + {lists:reverse(Passed), []}. + +validate_event2(_, [], _, _, []) -> + []; +validate_event2(_, [], _, _, Acc) -> + Acc; +validate_event2(DBRef, [Event|T], Which, WC, Acc) -> + ET = ((Event#'CosNotification_StructuredEvent'.header) + #'CosNotification_EventHeader'.fixed_header) + #'CosNotification_FixedEventHeader'.event_type, + CheckList = + case Which of + both -> + [ET]; + domain -> + [ET, + ET#'CosNotification_EventType'{type_name=""}, + ET#'CosNotification_EventType'{type_name="*"}]; + type -> + [ET, + ET#'CosNotification_EventType'{domain_name=""}, + ET#'CosNotification_EventType'{domain_name="*"}]; + _ -> + [ET, + ET#'CosNotification_EventType'{type_name=""}, + ET#'CosNotification_EventType'{type_name="*"}, + ET#'CosNotification_EventType'{domain_name=""}, + ET#'CosNotification_EventType'{domain_name="*"}] + end, + case check_subscription(DBRef, CheckList) of + true -> + validate_event2(DBRef, T, Which, WC, [Event|Acc]); + _-> + case catch cosNotification_Filter:match_types( + ET#'CosNotification_EventType'.domain_name, + ET#'CosNotification_EventType'.type_name, + WC) of + true -> + validate_event2(DBRef, T, Which, WC, [Event|Acc]); + _-> + validate_event2(DBRef, T, Which, WC, Acc) + end + end. + +check_subscription(_, []) -> + false; +check_subscription(DBRef, [H|T]) -> + case ets:lookup(DBRef, H) of + [] -> + check_subscription(DBRef, T); + _ -> + true + end. + + +%%------------------------------------------------------------ +%% function : filter_events +%% Arguments: A sequence of structured Events or #any +%% Returns : A tuple of two lists; list1 the events that passed +%% and list2 the events that didn't pass. +%%------------------------------------------------------------ + +filter_events(Events, []) -> + {Events, []}; +filter_events(Events, Filters) -> + filter_events(Events, Filters, [], [], false). + +filter_events(Events, [], false) -> + {Events, []}; +filter_events(Events, [], _) -> + {lists:reverse(Events), []}; +filter_events(Events, Filters, Reversed) -> + filter_events(Events, Filters, [], [], Reversed). + +filter_events([], _, AccPassed, AccFailed, false) -> + {lists:reverse(AccPassed), lists:reverse(AccFailed)}; +filter_events([], _, AccPassed, AccFailed, _) -> + {AccPassed, AccFailed}; +filter_events([H|T], Filters, AccPassed, AccFailed, Reversed) -> + case call_filters(Filters, H) of + true -> + filter_events(T, Filters, [H|AccPassed], AccFailed, Reversed); + _ -> + filter_events(T, Filters, AccPassed, [H|AccFailed], Reversed) + end; +filter_events(Any, Filters, _AccPassed, _AccFailed, _Reversed) -> + case call_filters(Filters, Any) of + true -> + {Any, []}; + _ -> + {[], Any} + end. + +call_filters([], _) -> + false; +call_filters([{_,H}|T], Event) when is_record(Event, any) -> + case catch 'CosNotifyFilter_Filter':match(H, Event) of + true -> + true; + _-> + call_filters(T, Event) + end; +call_filters([{_,H}|T], Event) when ?not_isConvertedAny(Event) -> + case catch 'CosNotifyFilter_Filter':match(H, + Event#'CosNotification_StructuredEvent'.remainder_of_body) of + true -> + true; + _-> + call_filters(T, Event) + end; +call_filters([{_,H}|T], Event) -> + case catch 'CosNotifyFilter_Filter':match_structured(H, Event) of + true -> + true; + _-> + call_filters(T, Event) + end. + + +%%--------------- END OF MODULE ------------------------------ |