Rest back-end and UIs

We have been spending the last four months moving our Win32 stand-alone applications to a more service oriented architecture. This new back-end  can be scaled to handle a wide variety of  TV Broadcasting enterprises. It has been a major movement from us, as many domain logic that was previously hardcoded into the desktop applications is now implemented into this back-end and shared among all clients. The logic is customizable per client basis through an scripting system.

We have targeted two desktop operating systems, MacOSX 10.6 and WindowsXP+ (in this order). These led to a new problem: developing a front-end that would offer the most user-friendly experience to the end user. Cocoa and WPF were the best choices and Monobjc provided a nice bridge to handle the Cocoa interface through Net/Mono. Silverlight was the third choice to those interfaces that could be also used from the web. The RESTful back-end allows us to easily develop and deploy any user interface while maintaining full compatibility within the installation with other clients.

Compared to AJAX and HTML5, Silverlight was the most cost-effective technology for us as we are used to write .Net code and we could use a large .net library we have already developed. So we have software (desktop applications for video editing, video review and cataloging) that share a common back-end and offer a native interface through Cocoa and WPF and some Web applications using Silverlight that also target MacOSX and Win32 clients but are more suited for the Web.

This huge project has been fun and having the chance to take part in almost any part of the new architecture (I have designed and implemented  part of our back-end and also being in charge of evaluating and teaching  Cocoa/Monobjc).

Advertisements

Monobjc and MonoMac

A few days ago Miguel de Icaza annocuded the availability of MonoMac, a new set of Binding to bring the Cocoa world to Mono.

There are others binding around, being Monobjc the most advanced one and more feature-rich from my point of view. The MonoMac approach is more dotNet-user friendly as it has .Net naming convention. This introduces a few requirements that I feel like a drawback more than an improvement. This naming convention needs a new and consistent documentation that allows the user to under the framework which will make the life harder if you are also reusing objective-c code, you cannot translate the code easily since you need a conversion step and You cannot either easily use the vast amount of cocoa documentation and examples found in the net, as the Cocoa api and MonoMac api are not named equally.

The other problem so far is the lack of examples and documentation in MonoMac, something that Monobjc project has solved from the early stages.

A new binding won’t do any good to the community, the user swill be spread around this two projects, lowering the final quality of both. We will continue with monobjc and we are looking forward to the new upcoming changes.

SynchronizationContext for Cocoa/Monobjc

Our current project does a heavy use of SynchronizationContext to be sure that all asynchronous notifications are executed in the proper thread. For our UI on Windows this means the main WPF’s thread which is provided by the framework. The problem raised when we started writing our Cocoa UI since we wanted to use the SynchronizationContext pattern we had been using on other platforms.

Monobjc provides a ISynchronizeInvoke interface implementation in NSView and is in fact quite straightforward to use but building your own SynchronizationCotext subclass for Cocoa
around the Monobjc bridge is also not that hard. The idea I had was to encapsulate the .Net callback and object state around an object (SyncObject) which inherits from NSObject.

Then we register our Objective-C callback into the main’s thread runloop by calling performSelectorOnMainThread:withObject:waitUntilDone: The ObjC callback is intercepted by the bridge which is then ran on the Mono side. There we can safely call the original .Net callback which is executed in the main thread.

The SyncObject can be (optionally) registered in a dictionary so that the Mono’s GC doesn’t have the chance to clean the object before the selector is executed. We unregister the SyncObject once the callback has been executed. This step is in fact unnecessary as the Monobjc bridge caches the object until a Release is intercepted from the ObjC side, that reference will keep the GC away from us. I have left the code commented just in case the implementation changes (which seems quite rare to happen…).

The idea can also be extended to any thread with a bit more work. If you extend this I would like to hear from you.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Monobjc.Cocoa;
using Monobjc;

namespace vsn.MacOSXComponents.Threading
{
    [ObjectiveCClass]
    public class SyncObject : NSObject
    {
        private SendOrPostCallback m_Callback;
        private Object m_State;

        public delegate void DeregisterDelegate(SyncObject sync_object);
#if USE_SYNCOBJS_DICTIONARY
        private DeregisterDelegate m_DeregisterDelegate;
#endif

public static readonly Class SyncObjectClass = Class.GetClassFromType(typeof(SyncObject));

        ///
        /// Initializes a new instance of the  class.
        ///
        public SyncObject() { }

        ///
        /// Initializes a new instance of the  class.
        ///
        ///

        public SyncObject(IntPtr nativePointer)
            : base(nativePointer) { }

        public override void  Dispose()
{
// This is here for testing purpouses
//Console.Write("Disposing SyncObject");
 	 base.Dispose();
}

        ///
        /// initializes the synchronization object with the given call back and object state
        ///
        /// callback
        /// state
        /// if true, the current thread waits until the operation is performed
        ///

        public void Initialize(SendOrPostCallback d, Object state, bool wait, DeregisterDelegate dereg_delegate)
        {
            m_Callback = d;
            m_State = state;
#if USE_SYNCOBJS_DICTIONARY
            m_DeregisterDelegate = dereg_delegate;
#endif

            // Call the NSObject's performSelector method from csharp code
        PerformSelectorOnMainThreadWithObjectWaitUntilDone(ObjectiveCRuntime.Selector("bridgedCallback:"), this, wait);
        }

        ///
        /// This is our callback which is invoked by the main thread's runloop
        ///
        [ObjectiveCMessage("bridgedCallback:")]
        public void BridgedCallback(NSObject param)
        {
            // Invoke the true callback
            m_Callback(m_State);         

#if USE_SYNCOBJS_DICTIONARY
    // Un register this object from our cache
            if (m_DeregisterDelegate != null)
                m_DeregisterDelegate(this);
#endif

// This region is unsafe as we are not protected against the GC. Monobjc
// has a cached reference to this object so shouldn't be a problem but
// could be a problem if the implementation changes.
// Consider using a CER
// GC.Collect(); // Testing

// Mark this object for deletion by an AutoreleasePool
this.Autorelease();
        }
    }

    ///
    /// Cocoa's SynchronizationContext implementation
    ///
    /// Only Post and Send methods are implemented
    ///
    public class CocoaRunLoopSynchronizationContext : SynchronizationContext
    {
        private Dictionary m_SyncObjectsDictionary;
        private readonly object m_lock = null;

        ///
        /// Default constructor
        ///
        public CocoaRunLoopSynchronizationContext()
            : base()
        {
            m_SyncObjectsDictionary = new Dictionary();
            m_lock = new object();
        }

        public override void Post(SendOrPostCallback d, Object state)
        {
    SyncObject sync = new SyncObject();
#if USE_SYNCOBJS_DICTIONARY
            lock(m_lock)
               m_SyncObjectsDictionary.Add(sync.GetHashCode(), sync);
#endif
            sync.Initialize(d, state, false, DeregisterSyncObject);

        }

        public override void Send(SendOrPostCallback d, Object state)
        {
            SyncObject sync = new SyncObject();
#if USE_SYNCOBJS_DICTIONARY
            lock (m_lock)
                m_SyncObjectsDictionary.Add(sync.GetHashCode(), sync);
#endif
            sync.Initialize(d, state, true, DeregisterSyncObject);
        }

        ///
        /// Called by SyncObject to remove itself from the dictionary so that no reference is held
        ///
        ///
target object to unregister
        private void DeregisterSyncObject(SyncObject sync_object)
        {
#if USE_SYNCOBJS_DICTIONARY
    lock (m_lock)
m_SyncObjectsDictionary.Remove(sync_object.GetHashCode());
#endif
        }
    }
}