Geeks With Blogs
Adam Sills Occasionally I come up with something worthwhile

A link between event controls in the iMotion Edgeware Platform represents the flow of data in the form of an event and delivered through a method invocation and is the primary way for event controls to “talk” to each other. This in-band communication may not always be the most appropriate way for one event control to execute another event control and an out-of-band method of communication may be more appropriate. Furthermore, there may also be a situation where removing controls from a workflow could alter the integrity of the workflow (a link may contain important information on processing the linked event control which could be lost if replacing it with another).

To accommodate these scenarios, the iMotion Edgeware Platform 5.2 has adopted a service provider framework similar to the service provider framework already available in the .NET Framework using the System.ComponentModel classes and interfaces.

Each workflow provides a container into which event controls may place services. This container can then be queried for services by event controls. This allows an event control to provide functionality to the rest of the workflow without establishing links to every event control.

What is a Service?

A service is a piece of functionality, typically defined by an interface, which is given to a service container. A service container provides a container for services to be added. A service provider is a mechanism for retrieving a service object. A client requesting access to a service will query the service provider to receive the service.

One example of a service would be the workflow context service that provides visibility into the event controls on a workflow and the links between them. Another example of a service would be one that allowed an event control to expose ASP.NET web services from directly within a workflow.

Design-Time vs. Runtime Services

An iMotion Edgeware Platform workflow operates within a runtime environment as well as a design environment. As such, services within iMotion can be targeted for a particular mode. A design-time service is available within the Event Workflow Editor. One such service provides additional details during a link operation to allow for custom link dialogs to be a little more aware of what they are linking to. This can be useful when a custom link dialog needs to be customized based on the event control being linked to. A runtime service, on the other hand, provides value during the execution of a workflow.

Intrinsic Services

Every workflow provides a number of services automatically to the controls hosted within it. Most are available both during design-time and runtime. The following is a listing of the services and their namespaces.

Globeranger.Event.IEventControlContextService
This service interface is used to provide access to additional information about the various IEventControls in the workflow including .NET Type Name, linked controls and initialization parameters.

Globeranger.Event.Design.ILinkOperationContextService
This service interface provides additional information about a link or an active link operation. This service is most useful when a registration argument dialog needs additional information about what event controls are actively being linked.

Getting Access to Services

When an event control is placed on a workflow design surface, an instance of the IEventControl-derived class is constructed. Before iMotion 5.2, the only thing this instance would do is provide custom dialogs. With iMotion 5.2 and above, you can also mark your class as being interested in design-time behavior using the Globeranger.Event.Design.IDesignAware interface. This interface defines three methods: AddingToDesigner, AddedToDesigner and RemovedFromDesigner.

The AddingToDesigner method gives you the ability to be notified when the control is about to be added and optionally disallow it from being added. The AddedToDesigner method gives you the ability to be notified when the control has been added. It is at this point that you should get access to the design-time services available. The RemovedFromDesigner method is called when your control has been deleted.

The following skeleton code illustrates these three methods:

public class MyAleConsumer : AbstractAleReportsEventConsumer,

      Globeranger.Event.Design.IDesignAware {

 

      private IEventControlContextService m_EventControlContextService;

      private ILinkOperationContextService m_LinkContextService;

     

      public MyAleConsumer() {

      }

 

            public void AddedToDesigner(IDesignEnvironment

designEnvironment) {

            IServiceProvider provider =

designEnvironment.GetServiceProvider();

            m_EventControlContextService =

(IEventControlContextService)

                  provider.GetService(

                        typeof(IEventControlContextService) );

            m_LinkContextService = (ILinkOperationContextService)

                  provider.GetService(

                        typeof(ILinkOperationContextService) );

      }

 

      public bool AddingToDesigner(IDesignEnvironment

designEnvironment) {

            // return false to disallow this control from being added

            return true;

      }

 

      public void RemovedFromDesigner() {

      }

}

Both the AddingToDesigner and AddedToDesigner methods are passed an instance of IDesignEnvironment which gives the event control access to the services provided by the Event Workflow Editor or other event controls in the workflow.

Intrinsic Services Example

The previous example shows how to get access to services through the IDesignEnvironment interface. There are three other main scenarios these two services provide. The first scenario is only allowing one control of a given type on a workflow.

public bool AddingToDesigner(IDesignEnvironment designEnvironment) {

      IServiceProvider provider =

designEnvironment.GetServiceProvider();

      IEventControlContextService svc = (IEventControlContextService)

            provider.GetService(typeof(IEventControlContextService));

 

      foreach( EventControlRef ec in svc.GetEventControls() ) {

            if( ec.Type == this.GetType() ) {

                  designEnvironment.LogEditorMessage(

                        EventLevel.Warn,

                        "Only one of this type is allowed!" );

                  return false;

            }

      }

 

      return true;

}

This code gets access to the IEventControlContextService and using it, loops through all event controls already in the workflow and disallows itself from being added if one already exists. When you attempt to add a second MyAleConsumer to a workflow, the control will not add to the canvas and you’ll see the following message.

 

The second scenario is further filtering the event controls you are linking to. Perhaps you have an event control that logically should not link to two of the same type of consumer. An example of this could be you are performing validation or verification of tags and processing results from two of the same type could provide issues in your business logic. To prevent something like this, you can alter your IsEventConsumerAllowed method to look at the already linked controls and return false if it is already linked.

public class MyAleFilter : AbstractAleReportsEventFilter,

      IDesignAware {

      private IEventControlContextService m_ContextService;

 

      public MyAleFilter() {

      }

 

      public override void Init(EventControlConfig config) {

      }

 

      public override void Shutdown() {

      }

 

      public override void ProcessAleReports(object sender,

            AleReportsEvent aleReportsEvent) {

      }

 

      public override bool IsEventConsumerAllowed(

IEventConsumer eventConsumer) {

            if( !base.IsEventConsumerAllowed(eventConsumer) ) {

                  return false;

            }

            EventControlRef thisRef =

m_ContextService.GetEventControlRef();

            EventControlRef[] linkedControls =

                  m_ContextService.GetLinkedToControls(thisRef);

            foreach( EventControlRef ec in linkedControls ) {

                  if( ec.Type == eventConsumer.GetType() ) {

                        return false;

                  }

            }

            return true;

      }

     

      public void AddedToDesigner(IDesignEnvironment designEnvironment)

{

            IServiceProvider provider =

                  designEnvironment.GetServiceProvider();

            m_ContextService = (IEventControlContextService)     

                  provider.GetService(

typeof(IEventControlContextService));

      }

 

      public bool AddingToDesigner(IDesignEnvironment

designEnvironment) {

            return true;

      }

 

      public void RemovedFromDesigner() {

      }

}

The third scenario is customizing a registration argument dialog based on the consumer being linked to. Perhaps as a producer you want to have different defaults depending on the type of control you are being linked to.

public IEventProducerLinkDialog GetDialog() {

      EventControlRef linkedTo = m_LinkService.GetLinkedTo();

      if( linkedTo.Type == typeof(MyKnownType) ) {

            return new MyProducerDialog( true );

      }

      return new MyProducerDialog( false );

}

In the preceding example the event control initializes a MyProducerDialog if the control being linked to is a MyKnownType instance with a constructor argument of true and false otherwise. This allows the producer to be a little smarter when dealing with the user interface since it has the capability to know what’s being linked to now.

Extrinsic Services

In addition to the services provided automatically by the framework, event controls can provide services to the rest of the workflow during both design- and run-times. The example services in the next couple sections are deliberately contrived to show how to implement your own service without getting bogged down in the details of what a service should do.

Providing a Service

This topic requires a lot of time and space so is outside the current scope. However, the iMotion Edgeware Platform 5.2 documentation will describe in detail how to do it. Instead, I am going to now focus on one of the new controls in iMotion 5.2 that takes advantage of this new API.

ASP.NET Host Service

In addition to the intrinsic services provided by the workflow runtime, iMotion 5.2 provides an event control which allows other event controls to register web services and other System.Web.IHttpHandler-derived classes. This event control can be found in the Runtime Services stencil and is called ASP.NET Host Service.

The ASP.NET Host Service creates an ASP.NET AppDomain and starts a simple web server to process requests.

 

This control provides a runtime service to the workflow through the Globeranger.Event.Services.Shared.Asp.IAspHostingService in the Globeranger.Event.Services.Shared.dll assembly. This service gives you the ability to add a web service handler using a standard .NET WebService or any IHttpHandler-derived class.

To use the ASP hosting service first requires a bit of understanding on how the ASP.NET AppDomain is set up. ASP.NET requires a dedicated AppDomain to run in and cannot inherit an existing AppDomain. This means that in order to host ASP.NET inside a workflow, a custom AppDomain must be created. This also means that out WebService Type must be able to talk across AppDomain boundaries in order to communicate with the event control. Because of this, an event control adding a web service handler must provide an AspDomainProxy. This proxy lives in the AppDomain the event control is hosted in, but is marshaled to the ASP.NET AppDomain and allows your WebService to communicate indirectly with your event control.

The first step to hosting a web service is to create your event control and AspDomainProxy subclass.

public class MyWebServiceProducer : AbstractAleReportsEventProducer {

      private string m_WebServiceName;

      private MyWebserviceProxy m_Proxy;

      private IAspHostingService m_AspService;

 

      public MyWebServiceProducer() {

      }

 

      public override void Init(EventControlConfig config) {

            m_Proxy = new MyWebserviceProxy( this );

      }

 

      public override void Shutdown() {

      }

 

      class MyWebserviceProxy : AspDomainProxy {

            private MyWebServiceProducer m_Producer;

 

            public MyWebserviceProxy( MyWebServiceProducer producer ) {

                  m_Producer = producer;

            }

 

            public void Process( Reports reports ) {

                  m_Producer.Broadcast(

                        new AleReportsEvent(reports)

                        );

            }

      }

}

The proxy here simply defines a Process method that accepts the ALE Reports class. The next step is to create the web service.

class MyWebservice : WebService {

      [WebMethod]

      public void ProcessAle( Reports reports ) {

            MyWebserviceProxy proxy = (MyWebserviceProxy)

                  AspDomainProxy.GetProxyInstance();

            proxy.Process( reports );

      }

}

When you need to get access to the proxy use the static AspDomainProxy.GetProxyInstance method to get the proxy associated with this web service. After retrieving the proxy, call its process method. This process method then sends the ALE reports to the producer’s Broadcast method.

Finally register the web service with the ASP Host Service.

[EventControlInitParameter("webserviceName",

typeof(string), true, 0,

"MyWebService.grws", "Webservice Endpoint Name")]

public override void Init(EventControlConfig config) {

      m_WebServiceName = config.InitParameters["webserviceName"];

      m_Proxy = new MyWebserviceProxy( this );

      m_AspService = (IAspHostingService) config.GetService(

            typeof(IAspHostingService));

      if( m_AspService == null ) {

            throw new InvalidOperationException(

                  "ASP hosting service required!" );

      }

      m_AspService.AddHandler(

            AspHandlerType.WebService,

            typeof(MyWebservice),

            m_WebServiceName,

            m_Proxy );

}

 

public override void Shutdown() {

      m_AspService.RemoveHandler( AspHandlerType.WebService,

            m_WebServiceName );

}

The Init method gets the web service name from from the initialization parameters. Note that the file extension is optional: the ASP Host Service uses .grws for web services and .grhx for all other IHttpHandler classes. The next lines get access to the IAspHostingService and throws an exception if it’s not available. Finally a handler is added using the IAspHostingService.AddHandler method. This is where the type of handler (AspHandlerType), the .NET Type to instantiate, the name of the service and the AspDomainProxy-derived proxy class are specified.

Finally, set up the ASP Host Service to use port 8089 and start the workflow, you can use a web browser to browse to http://localhost:8089/MyWebService.grws to see your web service hosted by the Event Workflow Editor.

What about .NET Framework 3.0 and the WCF?

The WCF definitely provides some of the same functionality as this ASP.NET host service. However, anyone stuck in .NET 1.1 using the iMotion Edgeware Platform 5.2 and who wants to host web services from within their workflows now get the ability.

Posted on Friday, June 30, 2006 12:57 PM iMotion Edgeware Platform , .NET , RFID , Technical | Back to top


Comments on this post: iMotion Edgeware Platform 5.2: Workflow Service Providers

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Adam Sills | Powered by: GeeksWithBlogs.net