Jump to: navigation, search

Tigerstripe Advanced Plugin Tutorial

Revision as of 09:49, 10 November 2011 by Nmehrega.cisco.com (Talk | contribs)

< To: Tigerstripe_Tutorials

This tutorial builds upon the concepts discussed in the Simple Plugin Tutorial. It introduces different rule types, shows how to incorporate more information into your templates and develop java helper classes to simplify template writing.

Defining Global Rules

In the Simple Plugin Tutorial we created a simple Artifact Rule. These rules are run once per artifact (for the the relevant Artifact Type) in the project, and produce a single file for each artifact.

In some situations, you may desire or need to create a file that contains information about multiple artifacts. For example, a single XML file that contains information about all of the artifacts in a project or an HTML index page that provides links to pages about each entity. In this case you would define a Global Rule.

Global Rules are executed once per generation of a project. There are a few differences from Artifact Rules: Creating a Global Rule follows the same process as creating an Artifact Rule, however there is less information to enter upon creation.

In the following example you will create a very simplistic XML schema that has elements for each Entity in the model. Note: The validity of this schema is questionable; it is intended purely as an example of a Global Rule.

To create a Global Rule:

1. Create a template in the templates directory of your Plugin Project. Name this template globalTemplate.vm and save the template. The template should include the following content:

     ## This is globalTemplate.vm
     <?xml version="1.0" encoding="UTF-8"?>
     <schema>
     #foreach ($entity in $entities)
         <complexType name="$entity.Name" >
               <complexContent>
                     <sequence>
     #foreach ($attribute in $entity.Fields)
                         <element name="$attribute.Name" type="$attribute.Type.Name" />
     #end
                     </sequence>
             </complexContent>
         </complexType>
     #end
     </schema>

Take look at a few key lines in this template:

  • #foreach ($entity in $entities) ...... #end - This line defines the enclosed template elements and ensures that they are iterated for each entity in the model.
    • $entities is one of a number of possible collections.
    • $enumerations, $datatypes etc, contain all instances of a particular artifact type.
    • $artifacts is the complete set of artifacts in the model.
  • #foreach ($attribute in $entity.Fields)..... #end - This line defines the enclosed template elements and ensures that they are iterated for each attribute defined in the entity.
    • Attributes are of type IField - more details can be found in the API documentation in the on-line Help.
    • Other useful collections are Literals for Constants and Methods for Methods.
  • type="$attribute.Type.Name" - Scans the model to obtain the name of the attribute type.

Note: You can obtain a more complete picture of the model structure by looking at the javaDoc for the Tigerstripe external API.

2. Create a Global rule that points to the template you created. You accomplish this in the same way you created an Artifact Rule. Refer to Define a Plug-in Rule for more information. There are actually two types of Global Rule - for this rule, select the "Simple Run Rule" from the drop-down list - this should be the default.

3. Click Save to save your changes.

The next time you generate a Project with this plugin enabled, you should get a schema file (schema.xsd) with content similar to the following:

<?xml version="1.0" encoding="UTF-8"?>
<schema>
   <complexType name="Order" >
         <complexContent>
               <sequence>
                   <element name="details" type="String" />
               </sequence>
       </complexContent>
   </complexType>
   <complexType name="Service" >
         <complexContent>
               <sequence>
                   <element name="Counter" type="int" />
                   <element name="ServiceName" type="String" />
               </sequence>
       </complexContent>
   </complexType>
</schema>

In this example, you have two entities named, Order and Service. The Service entity has two attributes, Counter and ServiceName.

It is important to realize that in a real model, you will see different content in the schema file but the structure will be the same and will be repeated for each entity.

Important: Global, Model and Artifact Rules can exist in the same plugin. Be careful to choose unique output file names.

Defining Model Based Rules

As a further variant of the rules we defined above, you may desire or need to create a file that contains information about the artifacts that exist in each "model" that is referenced from your own model project. In this case you would define a Model Based Rule.

Model Based Rules are executed once per model that is found in the depndency hierachy of a project. This will include any refernced modelas an and installed modules. The rule will walk the hiearchy tree to discover transitive depndencies in the tree. There are a few differences from Artifact Rules: Creating a Model Based Rule follows the same process as creating an Artifact Rule, however there is less information to enter upon creation.

In the following example you will create a very simplistic XML schema that has elements for each Entity in each model in teh dependecny tree. Note: The validity of this schema is questionable; it is intended purely as an example of a Model Based Rule.

To create a Model Based Rule:

1. Create a template in the templates directory of your Plugin Project. Name this template modelTemplate.vm and save the template. The template should include the following content:

     ## This is modelTemplate.vm
     <?xml version="1.0" encoding="UTF-8"?>
     <schema>
     #foreach ($entity in $moduleEntities)
         <complexType name="$entity.Name" >
               <complexContent>
                     <sequence>
     #foreach ($attribute in $entity.Fields)
                         <element name="$attribute.Name" type="$attribute.Type.Name" />
     #end
                     </sequence>
             </complexContent>
         </complexType>
     #end
     </schema>

This is very similar to the globalTemplate we saw above, but there is one key difference.

  • #foreach ($entity in $moduleEntities) ...... #end - The collection that is being iterated over is different! the $moduleEntities collection willbe limited to the entities in any given model in the tree.

2. When defining an output file Name for the rule, you will most likeley need to use the model Identity - something like ${modelId}.

Other than that, creating a model rule is very similar to creating a global rule.

Important: Global, Model and Artifact Rules can exist in the same plugin. Be careful to choose unique output file names.

Using Java Wrappers

(You can only specify a Wrapper within an Artifact Rule definition.)

A Wrapper is a Java class that implements the IArtifactWraper interface specified in the Tigerstripe External API. A wrapper is a set of code around a core Tigerstripe artifact that enables custom behaviour to be added to artifact. Appropriate use of a Wrapper can greatly simplify templates and enhance their readability.

In the Wrapper you define methods to manipulate the information available from the artifact, project etc, or look-up additional data. Some examples of method usage within a model are:

  • Reformat data - You may want to translate a package name to a directory path name. For example, com.mycompany.models could become com/mycompany/models.
  • Look up data in other related artifacts- eg If the type of an attribute is an enumeration, you can examine the Base Type of that enumeration (ie int or String).
  • Read additional data from a configuration file in the project or plugin.
  • Perform complex manipulations that would be difficult in Velocity.


The artifact that is to be processed is passed to the Wrapper using the setIArtifact method.


To create a new Wrapper:

  1. Create a new Java Class in the src directory of your plugin project.
  2. The Class must implement the IArtifactWrapper interface.
    • For this tutorial Name your model EntityWrapper in the com.mycompany.plugins.wrappers package
  3. Add the methods as outlined in the following example:


public class EntityWrapper implements IArtifactWrapper {
   private IArtifact artifact;  
   private IPluginConfig pluginConfig;
   public void setIArtifact(IArtifact artifact) {
       this.artifact = artifact;
   }
   public void setPluginConfig(IPluginConfig pluginConfig) {
       this.pluginConfig = pluginConfig;
   }
   public IArtifact getArtifact(){
       return artifact;
   }
   public String getName(){
       return getArtifact().getName()+"Value";
   }
   public String getKeyName(){
       return getArtifact().getName()+"Key";
   }
   public String getOutPath(){
       String packageName = getArtifact().getPackage();
       String path = packageName.replaceAll("\\.", "/");
       return path;
   }

}

Note: You will need to "import" the IArtifactWrapper, IPluginConfig and IArtifact interfaces for the Tigerstripe External API.

In this example, you will specify the type of your artifact to be IArtifact. This allows the model to be used for any artifact type. In many cases however, you may wish to create a more specific type of artifact, such as IManagedEntityArtifact. This allows access to methods on a particular artifact type. It is also possible to create a generic wrapper and inherit from that wrapper for each specific artifact type.

Note that through the pluginConfig object that is passed, you can gain access to project properties.

Having created your wrapper, you will need to refer to it in a template and then set up a rule to pass the model to the template.

To reference your wrapperin a template:

1. Create your template. This procedure is the same as previously outlined, but within the template you will make reference to methods on the wrapper.

## This is wrapperTemplate.vm
##
// This file was generated using $templateName.
##
// In this case the artifact Name ($artifact.Name) ,
// is not the same as the model Name ($wrapper.Name).
public interface ${wrapper.Name} {
   public String get${wrapper.KeyName};
} 


2. Create a new rule as described in the Simple Plugin Tutorial. However, in the Wrapper Class field, browse to the Wrapper Class that you created above. In the Wrapper Class Name text box, enter the name by which the wrapper will be referred to within your template.

  • In this tutorial, name your Wrapper class wrapper . Hence the use of $wrapper in the above template example.
  • Note: The Output File definition can contain references to wrapper methods by using the ${model.xxx} syntax.

3. Click Save to save your plug-in project. 4. Generate against your Tigerstripe project.

The generated file for the Order entity will be in a directory based on it's package name, and will have the name OrderValue.out. It will contain a method called getOrderKey.

You could have easily achieved this result by using template functions, but as your processing becomes more and more complex, wrappers will become an essential part of your plug-ins.

Note: IArtifactWrapper replaces the old IArtifactModel which had the same basic behaviour.

Artifact Filters

(This feature only applies to Artifact Rules)

Sometimes you may need to run through a subset of artifacts of a given Artifact type. Rather than code a rule into your template, you can specify an Artifact Filter that will limit the artifacts that are processed in the template. This will keep your templates simple and easy to understand.

Artifact Filters are defined in a similar manner to [[Tigerstripe_Glossary#Model Class|Models]. That is, Artifact Filters are Java classes in the src directory of your plugin that implement the IArtifactModel interface. The filter needs to implement a single select method and any artifact that returns a result of true, is passed to the template engine for further processing.

In the following filter, only artifacts that have a Stereotype of name Version are accepted and all other artifacts are rejected. For a detailed discussion of Stereotypes see the Advanced Stereotypes to Tigerstripe Models tutorial.


public class VersionFilter implements IArtifactFilter {
   public boolean select(IArtifact artifact) {    
       IextStereotypeInstance[] stereos = artifact.getStereotypeInstances();
       for (IextStereotypeInstance instance: stereos){
           if (instance.getName().equals("Version")){
               return true;
           }
       }
       return false;
   }
}

Once you have defined a filter, you can apply that filter to an artifact rule by clicking on Browse next to the Artifact Filter field in the plugin descriptor editor, Rules tab.

Note: Remember to deploy your plug-in when you change a rule definition.

Velocity Context Definitions

Another way to simplify templates is to create a Utility class. A Utility class contains commonly used methods; a typical case might be a set of String manipulation utilities. These could be third party utilities or your own.

To write your own utility:

1. Create a class in the src directory of your plugin project.

  • For this tutorial, name your class 'MyUtils'.

2. Add methods as appropriate to your class. You can add as many methods as you like, but for this example you will create a single method (shown below):

public class MyUtils {  
   public String capitalize(String inString){
       return inString.substring(0,1).toUpperCase() +
              inString.substring(1);    
   }
}

3. Add Utility classes to your rule by adding them to the list of Velocity Context Definitions. Each definition (either your own or a reference to a .jar containing utilities) must have an entry with a name and a path . The name is used in the template to identify the specific utility class.

In your template, you can then use these methods with the $utilName.methodName(arg) syntax. For example, using the utility as defined above, which has been added with the name myUtils:

public $attribute.Type.Name get${myUtils.capitalize($attribute.Name)}();

Note: In this case the ${ } syntax for Velocity can help when your utility function is adjacent to other text.

If you call a method that does not exist, Velocity will simply leave the reference in place. For example, if you mistype the above function name and run your plugin, you may find a line (such as the one outlined below) in your output file:

public String get${myUtils.capitulate(Details)}();

Logging and reporting statuses

org.eclipse.tigerstripe.workbench.plugins.PluginLog class provides ability to inform end user about generation details. You can use PluginLog.log* methods to log a message into the plugin specific log file. To display a message into Generate Result dialog box your should use PluginLog.reportStatus(IStatus) method.

private static string PLUGIN_ID = "plugin.id"
public class MyUtils {  
   public String convert(String input) {
       PluginLog.log(LogLevel.DEBUG, "converting " + input);
       try {
          return doConversion(input);
       } catch (Exception e) {
          Pluginlog.reportStatus(new Status(IStatus.ERROR, PLUGIN_ID, "error during conversion", e));
       }
       return null;    
   }
}

Velocity Macros

Velocity supports the use of Velocimacros to simplify repetitive template functions. You can find out more about these macros on the Velocity website.

Velocimacros are added to a rule definition in a very similar manner toVelocity Context Defintions. Simply navigate to the Velocity Macro section of the rule editor and click on "Add".

Note: Tigerstripe does not support the definition of "in-line macros.

Relationship between Rules, Templates , Velocity macros, and Velocity context definitions.

The basic scenario is that, when a rule runs a set of objects are passed to velocity which renders the output, as described in the template specified for that rule.

The set of Java objects that are passed to velocity is the "context".

A rule *always* sends a fixed set of java objects to velocity - these are described in the section titled "Default Velocity Context Definitions" in the miscellany tutorial. Looking at this, you can see that the default context contains things such as the $project, $pluginConfig, and $artifact (for an artifact rule). There are the objects that you will use in a template.

The template only knows about those objects which the rule passes to it, and can only operate over those items.

By specifying additional "Velocity Context Entries" in a rule, you can increase the set of objects which the template can access. This is most often used for passing utility programs in that will be used for formatting data, or access external data.

As an example you might create a java utility class called MyUtils that reads an external xml file and makes the data available through some static methods such as getMyData(). By adding the class MyUtils to the list of Velocity Context Definitions in the rule, and giving it the name of "myUtils", in your template you can use $myUtils.MyData to execute the method and thus access your xml data.

Velocity Macros are a built-in velocity feature that can be used for writing a common function in the template. For example in a template that is generating java, you might have a fixed way of outputting comments - ie in a /*.....*/ block. This format requires that the text is surrounded by the right comment syntax , is at a fixed indent, the line is truncated to a given length and so on.Rather than write this code at every point you want a comment, you could write a macro that takes some text and renders the text in the right way. Every time you have a comment you simply call the macro, passing it the text of the comment as an argument.


Plug-in Reports

Each time you click Generate' in a Tigerstripe Project, you can obtain a Tigerstripe Report in the target directory of your project. You can enable or disable this feature though the Advanced pane of the Tigerstripe Project Descriptor (tigerstripe.xml) Editor.

If you enable this feature, the Tigerstripe Report will appear as TigerstripeReport.xml, and will include (in addition to other information about the project) a section for each Rule. These reports can be useful at runtime to determine what has been handled, filtered, and generated. An example extract from a plugin with a single Rule is shown below:

<pluginReports>
    <pluginReport    group   = ""
                     id      = "SimplePluginProject()"
                     version = "" >
           <property directoryName = "default" />
           <property flag = "false" />
           <childReports>
               <childReport
                   ruleName = "modelRule"
                   ruleType = "Artifact Model Run Rule"  >
                   <matchedArtifacts>
                       <matchedArtifact name = "com.mycompany.Order"/>
                       <matchedArtifact name = "com.mycompany.Service"/>
                   </matchedArtifacts>    
                   <generatedFiles>
                       <file name = "com/mycompany/OrderValue.out"/>
                       <file name = "com/mycompany/ServiceValue.out"/>
                   </generatedFiles>
               </childReport>
           </childReports>
           </pluginReport>
   </pluginReports>

In this example, the initial section display information about the plugin', and the value of the Global Properties that have been set for this project. As a result, a childReport is created for each Rule in the plugin.

  • <matchedArtifacts> lists the qualified name for the artifacts that were passed to this rulebased on their Artifact Type and passed any defined filter.
  • <generatedFiles> lists the files generated relative to the output directory for your project.

Note : This description is not complete and there will be additional information in the report.