Tuesday, November 22, 2005

One of the things I *really* want to implement for our DSL is “round tripping”. This means in my case, (at least) generate a “Service Description diagram” out of a valid WSDL file and visa versa.

 

Some weeks ago I already did an attempt in this direction but eventually I ran into problems with the serializer. As you can read in this post on the DSL forum the relations between concept got lost when trying to serialize (save to disk) the in-memory representation of the generated diagram. Probably this is caused by either a mistake on my side or a bug in the DSL Tools. Some days ago while playing with the propagation rules I found a way to make “round tripping” work in our DSL. Let me explain a little further.

 

I added a new property to the “Service” concept in the DSL, called “WsdlDescription”. This property represents a reference to a WSDL file describing the service. I added a new propagation rule that fires whenever the “WsdlDescription” property of the Service concept changes. This propagation rule, on its turn, fires a piece of code that parses the WSDL file (referenced by the “WsdlDescription” property) and generates a diagram out of this. With this code in place I can generate a diagram out of a WSDL file by simply creating a new empty diagram. To be more precise, adding a new item to the Visual Studio solution based on the “ServiceDescription” (name of the DSL) template. This results in an (valid) empty diagram. I can set the “WsdlDescription” property (service concept) in the empty diagram to reference a valid WSDL file and the magic happens, the diagram is generated! The advantage of this approach, compared to creating an empty diagram from scratch by using custom code, is that we can rely on the DSL Tools infra structure to properly initialize the diagram.

 

Of course you can find another “hook” to implement your round tripping code. This depends on the DSL you are building. For this moment, in our DSL, the propagation rule on this property does the trick.

 

Let’s have a look at some code. Below you see the code for the propagation rule that is defined for the “Service” concept in the model. First we try to cast the input parameter of the “ElementAttributeChanged” method to a “Service”. If that succeeds and the rule is fired because of a change in the “WsdlDescription” property we can continue. To create any new concepts in the diagram we need the “Store”. This can be seen as the in-  memory representation of the diagram. We can get this by using the “Store” property on the “Service”. Then we create a “ServiceInterface and ServiceOperation and register both elements under the Service by using the “ServiceArtifacts” relation. (To better understand the model of this DSL, have a look at the, slightly outdated, image of the domain model of the DSL). Finally we add the “ServiceOperation” as an “Operation” under the “ServiceInterface” and we are done. We now created two model elements and a relation between them in code. All displayed perfectly in the diagram.

 

[RuleOn(typeof(OurCompany.Design.ServiceDescription.DomainModel.Service),
                                                     FireTime
= TimeToFire.TopLevelCommit)]
    
public sealed class DomainModelServicePropertiesChangesRule : ChangeRule
    {
        
public override void ElementAttributeChanged(ElementAttributeChangedEventArgs e)
        {
            
            
// cast input parameter to Service element
            OurCompany.Design.ServiceDescription.DomainModel.Service service
=
                                 e.ModelElement
as OurCompany.Design.ServiceDescription.DomainModel.Service;
            
if (service != null)
            {

                
// only execute it when the rules is fired because of a change in the WsdlDescription property.
                
if (e.MetaAttribute.Id ==
                        OurCompany.Design.ServiceDescription.DomainModel.Service.WsdlDescriptionMetaAttributeGuid)
                {
                    
                    
// create new ServiceInterface. Use the store of the service
                    ServiceInterface serviceInterface
=                                                                 ServiceInterface.CreateServiceInterface(service.Store);
                    serviceInterface.Name
= "GeneratedInterface";
                    
                    
// add ServiceInterface as an ServiceArtifact of the service
                    service.ServiceArtifacts.Add(serviceInterface);

                    
// create new ServiceOperation. Use the store of the service
                    ServiceOperation serviceOperation
= ServiceOperation.CreateServiceOperation(service.Store);
                    serviceOperation.Name
= "GeneratedServiceOperation";

                    
// add ServiceOperation as an ServiceArtifact of the service
                    service.ServiceArtifacts.Add(serviceOperation);

                    
// add the relation from serviceInterface to serviceOperation
                    serviceInterface.ServiceOperation.Add(serviceOperation);
                }
            }


        }

    }




In the above code snippet all of the diagram generating is hard coded, this is, of course, just for this example. In my situation the hard coded stuff is replaced by some calls to helper functions that parse the WSDL and translate this back in a diagram.

 

To make sure the rules gets executed on changes in the Service concept we need to facilitate that it can be “picked up” by the “rule engine”. This is done by reflection; therefore we need the following code.

    // This class is reflected on to load rules dynamically.
    
// Add a variable for each custom rule.
    
internal static partial class GeneratedMetaModelTypes
    {
        
internal static Type DomainModelServicePropertiesChangesRule =
                                    
typeof(DomainModelServicePropertiesChangesRule);
    }

 

Now the diagram is generated out of the WSDL, we have to make sure the WSDL stays in synch with any changes that might be made in the diagram. It’s my intention to let this synchronisation magic happen automatically, so without any user actions (pressing menu options, etc.). To experiment with this I added the “synchronization logic” to the “OnTransactionCommiting” event of the diagram. As you can see in the code snippet below this is just a matter of overriding the “OnTransactionCommiting” method in a partial “ServiceDescriptionDiagram” class.

 

public partial class ServiceDescriptionDiagram
{
   public override void OnTransactionCommitting(TransactionCommitEventArgs e)
   {
      base.OnTransactionCommitting(e);

      //validate the diagram and execute synch code


   }

}

 

Although this works perfect I am not absolutely sure if this is the right place to implement this kind of logic. I noticed that the code gets fired a lot. Within this method I first validate the diagram to make sure there is enough information in the diagram to generate a valid WSDL file. If so, the generation (synchronisation) starts. I haven’t experienced any performance issues with this approach but maybe it is better to only synch when the diagram gets saved. I have to do some more investigation on this area.

 

To be continued….

 

posted on 11/22/2005 8:24:24 PM UTC  #    Comments [0]
 Sunday, November 20, 2005

 

The MessageHeader concept in our DSL has an attribute “SchemaLocation” (the same is true for Message and DataContract). This property is a reference to the XSD description of the MessageHeader. Another property of the Message concept is the “Namespace” property. The value of this property is used in the WSDL that is generated out of the Domain model of our DSL. In an attempt to make our DSL as user friendly as possible I decided to set the “Namespace” property automatically as soon as the “SchemaLocation” is populated.

 

To make this possible I wrote a propagation rule (one of the customization features of the DSL Tools) to make this happen.

 

Below you can see the code that is needed for a propagation rule. I stored this code in a separate .cs file in a folder called “custom” that I created in the designer project. The rule inherits from “ChangeRule”. The implementation isn’t that difficult (note: just demo code)

 

[RuleOn(typeof(OurCompany.Design.ServiceDescription.DomainModel.MessageHeader),
FireTime = TimeToFire.TopLevelCommit)]
public sealed class DomainModelMessageHeaderPropertiesChangesRule : ChangeRule
{
   public override void ElementAttributeChanged(ElementAttributeChangedEventArgs e)
   {
      // store the Namespace in
      string messageHeaderNamespace;


      OurCompany.Design.ServiceDescription.DomainModel.MessageHeader messageHeader =
      e.ModelElement as OurCompany.Design.ServiceDescription.DomainModel.MessageHeader;
      

      if (messageHeader != null)
      {
         if (e.MetaAttribute.Id ==
         OurCompany.Design.ServiceDescription.DomainModel.MessageHeader.SchemaLocationMetaAttributeGuid)
         {

            if (!String.IsNullOrEmpty(messageHeader.SchemaLocation))
            {

               // helper code to retrieve the Namespace from the XSD 
               ServiceDescriptionEngine.GetSchemasFromXsd(messageHeader.SchemaLocation, out messageHeaderNamespace);


               // set the value of the Namespace property 
               messageHeader.Namespace = messageHeaderNamespace;

            }

         }   

      }
}

}

 

 

Because the code that actually retrieves the “Namespace” out of the XSD file is stored in another assembly that isn’t part of the DSL assemblies, I had to add the assembly to the GAC to make it work.

 

To make sure the rule gets fired we need to add a variable to the “GeneratedMetaModelTypes” class. This class is reflected on to dynamically load rules. The variable is the reference to the new propagation rule described above. The following code snippet will do the trick.

 

Internal static partial class GeneratedMetaModelTypes
{
        
internal static Type DomainModelMessageHeaderPropertiesChangesRule =
                                    
typeof(DomainModelMessageHeaderPropertiesChangesRule);
}

 

As you can see in the image below (I know this image doesn’t prove a lot!) the “Namespace” property gets set after we populate the “SchemaLocation” property of the MessageHeader with a valid value.

To be continued...

posted on 11/20/2005 8:12:43 PM UTC  #    Comments [2]
 Friday, November 18, 2005

Because we made some good progress with building the core functionality of our DSL I decided to spend some time on the customization options of the DSL Toolkit.

I tried some of the customizations described in this document that can be found on the DSL Tools forum and also implemented the “ToolTipText” for all of the shapes and connectors by using the code that is described in this post of Pedro Silva.

Another thing that I found was necessary was changing the color scheme of the Shapes. I decided to use some RGB colors and found out that using an “RGB color code” in the designer.dlsdd file isn't an option. It wasn't too hard to found out how this can be achieved by writing some lines of custom code. As you can see in the code snippet below it is just a matter of overriding the FillColor property in a partial class for the Shape.

public partial class ServiceOperationShape
{

    
public override Color FillColor
    {
        get
        {
            
return Color.FromArgb(132, 150, 172); 
        }
            
    }

}

The Message concept in our DSL is holding an embedding relation for the DataContract and MessageHeader concept. Therefore our MessageShape is a compartment shape this means for these two compartments there isn’t a possibility to override the FillColor property in a partial class.

 

We had to find another way to changes the color. It turns out that compartments can be reached in custom code by using the “compartmentDescriptions” collection on the parent shape. As you can see in the code snippet below I used the “OnInitialized” of the MessageShape (parent) to set the “TitleFillColor” property of the two compartments in the MessageShape to an RGB color code. (Maybe there is a better place to set the color but this does work. Another option would be the “ OnShapeInserted“)

 

public partial class MessageShape

{

 

      public override void OnInitialized()

      {

 

            ...

 

MessageShape.compartmentDescriptions[0].TitleFillColor = Color.FromArgb(132,150,173);

 

MessageShape.compartmentDescriptions[1].TitleFillColor = Color.FromArgb(189,162,123);

      }

 

}

 

As you can see in the image below the color of the compartments are changed (compared to the colors in some previous posts). Also a “TooltipText” is displayed when moving the mouse over the connector shape. The tooltipText is composed out of the information of the two shapes that are connected by the connector.

 

To be continued...

posted on 11/18/2005 10:14:57 PM UTC  #    Comments [2]