/// <summary> /// Performs a subscription to a remote publisher for a local subscriber<br /># /// In this method, the remote subscription is completed /// </summary> internal static void SubscribeToRemotePublisher <T>(RemoteSubscriptionHandle handle, object subscriberInstance, Action <T, SubscriptionHandle> newDataCallBack, DataModifyPolicy policy, Func <T, bool> evaluateTemplateObject, Action <Type, SubscriptionHandle> subscriptionCallback) { _log.DebugFormat("Completing subscription to remote publisher {0} on node {1},handle: {2}, type: {3}", handle.PublisherId, handle.PublisherNodeID, handle, typeof(T).Name); //TODO template object //Create a stub Stub <T> s = new Stub <T> { DataType = typeof(T), Handle = handle }; if ( (EllaModel.Instance.GetActivePublishers().Where(p => p.Instance is Stub && (p.Instance as Stub).Handle == handle)).Any()) { _log.DebugFormat("Avoiding double subscription for handle {0}. Anready an active stub available.", handle); return; } var publishesAttribute = (PublishesAttribute)s.GetType().GetCustomAttributes(typeof(PublishesAttribute), false).First(); if (handle is MulticastRemoteSubscriptionhandle) { _log.DebugFormat("{0} is potential multicast subscription, opening multicast port", handle); MulticastRemoteSubscriptionhandle mh = handle as MulticastRemoteSubscriptionhandle; Networking.ConnectToMulticast(mh.IpAddress, mh.Port); RemoteSubscriptionHandle h = new RemoteSubscriptionHandle() { PublisherNodeID = handle.PublisherNodeID, PublisherId = handle.PublisherId, SubscriptionReference = handle.SubscriptionReference, EventID = handle.EventID, SubscriberNodeID = handle.SubscriberNodeID }; s.Handle = h; } Start.Publisher(s); Event ev = new Event { Publisher = s, EventDetail = publishesAttribute }; handle.SubscriberId = EllaModel.Instance.GetSubscriberId(subscriberInstance); Subscription sub = new Subscription(subscriberInstance, ev, newDataCallBack.Method, newDataCallBack.Target) { Handle = s.Handle, DataType = typeof(T) }; EllaModel.Instance.AddSubscription(sub); var sanity = EllaModel.Instance.CheckSubscriptionSanity(); if (sanity > 0) { _log.WarnFormat("Subscription sanity restored. removed {0} subscriptions", sanity); } if (subscriptionCallback != null) { _log.DebugFormat("Found subscription callback for {0}, notifying subscriber", handle); subscriptionCallback(typeof(T), sub.Handle); } else { _log.DebugFormat("Subscription callback for {0} was null, not notifying subscriber", handle); } }
/// <summary> /// Does the local subscription. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="subscriberInstance">The subscriber instance.</param> /// <param name="newDataCallback">The new data callback.</param> /// <param name="evaluateTemplateObject">The evaluate template object.</param> /// <param name="subscriptionCallback">The subscription callback.</param> /// <param name="policy">The modification policy.</param> /// <exception cref="System.ArgumentException">subscriberInstance must be a valid subscriber</exception> /// <exception cref="IllegalAttributeUsageException"></exception> internal static void DoLocalSubscription <T>(object subscriberInstance, Action <T, SubscriptionHandle> newDataCallback, Func <T, bool> evaluateTemplateObject, Action <Type, SubscriptionHandle> subscriptionCallback, DataModifyPolicy policy) { /* * find all matching events from currently active publishers * check if subscriber instace is valid subscriber * hold a list of subscriptions */ if (!Is.Subscriber(subscriberInstance.GetType())) { _log.ErrorFormat("{0} is not a valid subscriber", subscriberInstance.GetType().ToString()); throw new ArgumentException("subscriberInstance must be a valid subscriber"); } var matches = EllaModel.Instance.ActiveEvents.FirstOrDefault(g => g.Key == typeof(T)); if (matches != null) { Dictionary <SubscriptionHandle, SubscriptionHandle> correlatedEvents = new Dictionary <SubscriptionHandle, SubscriptionHandle>(); MethodBase associateMethod = ReflectionUtils.GetAttributedMethod(subscriberInstance.GetType(), typeof(AssociateAttribute)); _log.DebugFormat("Found {0} matches for subsription to {1}", matches.Count(), typeof(T)); foreach (var m in matches) { if (m.Publisher == subscriberInstance) { _log.DebugFormat("Not subscribing {0} for itself", subscriberInstance); continue; } object templateObject = Create.TemplateObject(m.Publisher, m.EventDetail.ID); T template = templateObject != null ? (T)templateObject : default(T); //SubscriptionID in the handle is set automatically when assigning it to a subscription SubscriptionHandle handle = new SubscriptionHandle { EventID = m.EventDetail.ID, PublisherId = EllaModel.Instance.GetPublisherId(m.Publisher), SubscriberId = EllaModel.Instance.GetSubscriberId(subscriberInstance) }; var subscription = new Subscription { Event = m, Subscriber = subscriberInstance, CallbackMethod = newDataCallback.Method, CallbackTarget = newDataCallback.Target, Handle = handle, DataType = typeof(T), ModifyPolicy = policy }; if (templateObject == null || evaluateTemplateObject(template)) { if (!EllaModel.Instance.ContainsSubscriptions(subscription)) { _log.InfoFormat("Subscribing {0} to {1} for type {2}", subscriberInstance, m.Publisher, m.EventDetail.DataType); EllaModel.Instance.AddSubscription(subscription); if (subscriptionCallback != null) { subscriptionCallback(typeof(T), subscription.Handle); } NotifyPublisher(subscription.Event, subscription.Handle); if (associateMethod != null) { var correlations = EllaModel.Instance.GetEventCorrelations(handle.EventHandle); if (correlations != null) { foreach ( SubscriptionHandle correlationHandle in correlations.Select(correlation => new SubscriptionHandle() { EventHandle = correlation, })) { correlationHandle.SubscriberId = handle.SubscriberId; correlatedEvents.Add(handle, correlationHandle); } } } } } else { _log.DebugFormat("Templateobject from {0} was rejected by {1}", m.Publisher, subscriberInstance); } } if (associateMethod != null) { if (associateMethod.GetParameters().Count() != 2 || associateMethod.GetParameters().Any(p => p.ParameterType != typeof(SubscriptionHandle))) { throw new IllegalAttributeUsageException(String.Format("Method {0} attributed as Associate has invalid parameters (count or type)", associateMethod)); } foreach (var handlePair in correlatedEvents) { //Only do this if subscriber is subscribed to both events if (EllaModel.Instance.FilterSubscriptions(s => Equals(s.Handle.EventHandle, handlePair.Value.EventHandle) && s.Subscriber == subscriberInstance).Any()) { associateMethod.Invoke(subscriberInstance, new object[] { handlePair.Key, handlePair.Value }); } } } } }
/// <summary> /// Subscribes the <paramref name="subscriberInstance" /> to any event matching <typeparamref name="T" /> as event data type /// </summary> /// <typeparam name="T">The type to subscribe to</typeparam> /// <param name="subscriberInstance">The instance of a subscriber to be subscribed to the event</param> /// <param name="newDataCallback">A callback method acceptiong <typeparamref name="T" /> as argument, which will be called when new data from publishers is available</param> /// <param name="policy">The data modify policy, default is <see cref="DataModifyPolicy.NoModify" /></param> /// <param name="evaluateTemplateObject">Pass a Func to subscribe using template objects. If no Func is given, <paramref name="subscriberInstance" /> will be subscribed to every event with matching <typeparamref name="T" />.<br /> /// As an alternative, a <paramref name="evaluateTemplateObject" /> can be provided to request templates for the data to be published from every single publisher.</param> /// <param name="forbidRemote">if set to <c>true</c> no remote publishers will be considered.</param> /// <param name="subscriptionCallback">A callback method used to notify the subscriber of a new subscription. It passes a <seealso cref="SubscriptionHandle"/> instance used to identify the 1:1 relation between one publisher event and one subscriber</param> /// <exception cref="System.ArgumentException">subscriberInstance must be a valid subscriber</exception> public static void To <T>(object subscriberInstance, Action <T, SubscriptionHandle> newDataCallback, DataModifyPolicy policy = DataModifyPolicy.NoModify, Func <T, bool> evaluateTemplateObject = null, bool forbidRemote = false, Action <Type, SubscriptionHandle> subscriptionCallback = null) { _log.DebugFormat("Subscribing {0} to type {1} {2}", subscriberInstance, typeof(T), (evaluateTemplateObject != null ? "with template object" : string.Empty)); EllaModel.Instance.AddActiveSubscriber(subscriberInstance); if (!forbidRemote) { if (Networking.IsRunning) { Func <T, bool> eval = evaluateTemplateObject; Action <RemoteSubscriptionHandle> callback = handle => SubscriptionController.SubscribeToRemotePublisher(handle, subscriberInstance, newDataCallback, policy, eval, subscriptionCallback); Networking.SubscribeToRemoteHost <T>(callback); } } if (evaluateTemplateObject == null) { evaluateTemplateObject = (o => true); } SubscriptionRequest sr = new SubscriptionRequest() { SubscriberInstance = subscriberInstance, RequestedType = typeof(T) }; sr.SubscriptionCall = () => SubscriptionController.DoLocalSubscription <T>(subscriberInstance, newDataCallback, evaluateTemplateObject, subscriptionCallback, policy); EllaModel.Instance.AddSubscriptionRequest(sr); SubscriptionController.DoLocalSubscription(subscriberInstance, newDataCallback, evaluateTemplateObject, subscriptionCallback, policy); }