Use the topology editor in Rational Software Architect to add a custom validator

Validation mechanism options for your custom technology domain

Validators enforce conditions on a model element in a topology, create an error message in the diagram, and suggest an optional resolution to correct the condition. This tutorial describes how you can add a custom validator to IBM® Rational® Software Architect.

André Zwanziger (andre.zwanziger@arch-IT-ecture.com), IT Architect, IT Architecture Consulting Group, Magdeburg

author photoAndré Zwanziger is an IT architect for the IT Architecture Consulting Group in Magdeburg, Germany. He is doing his PhD dissertation at the University of Magdeburg, and his research topics include the planning and automation of software deployment.



Chad Holliday (chadh@us.ibm.com), IBM Rational Software Development Team, IBM

author photoChad Holliday is a member of IBM Rational Deployment Architecture Platform team, and is the creator of the Topology Domain Generator Toolkit. Holliday has a B.S. degree from the University of Illinois at Urbana-Champaign. Contact Chad at chadh@us.ibm.com.



Sebastian Herden (sebastian.herden@arch-it-ecture.com), IT Architect, IT Architecture Consulting Group, Magdeburg

author photoSebastian Herden is an IT architect for the IT Architecture Consulting Group in Magdeburg, Germany. He is doing his PhD dissertation at the University of Magdeburg, and his research topics include the planning and automation of software deployment.



Tim McMackin (tmcmack@us.ibm.com), Technical Writer, IBM

Author photoTimothy McMackin is a technical writer for IBM's Rational software in Raleigh, NC. McMackin has a background in writing for advertising technical products and has been with IBM since 2004. Contact Tim at tmcmack@us.ibm.com.



23 August 2011 (First published 08 March 2011)

Also available in Chinese

How validators look for topology problems

This tutorial adds to one previously published titled Extending the IBM® Rational® Software Architect topology editor with custom technology domains. It explained how to create a custom MySQL domain with topology units and capabilities that represent components of a MySQL system. This article shows how to add custom validators to that domain.

Validators ensure that a topology represents a valid deployment scenario and help you identify problems with the scenario early in the deployment process. The topology editor includes a variety of validators that constantly look for problems in topologies. For example, one of the default validators checks to make sure that each unit with a hosting requirement also has a hosting link to an appropriate unit (see Figure 1).

Figure 1. Screen capture of a hosting error
Error: Tomcat unit with a missing hosting link

However, the varied nature of the topology editor domains means that individual unit types can behave in unique ways. You can create validators for your custom domains to ensure that the units and other topology elements in those domains are used properly.

Custom validators can check for problems in multiple scopes:

  • Attribute validators verify that a single attribute is set to a valid value, length, format, or data type. For example, this article shows how a validator can verify that a version number attribute follows a version number format, such as 4.0.2 or 5.26.0.
  • Unit validators verify that a single unit is valid. Unit validators can check the capabilities, requirements, attributes, and links on the unit and ensure that the unit is properly configured as a whole.
  • Domain validators verify relationships between two or more units in the topology. For example, domain validators can verify that there are no duplicated values in the topology, such as port numbers or network addresses. Domain validators also enable lower level validators, such as the unit validators.
  • Constraint validators, which validate specialized relationships and characteristics of model elements, are also available, but are beyond the scope of this article.

When a validator discovers a problem in a topology, it can add one or more error, warning, or informational messages to elements in the topology. (Validators can also provide resolutions that can correct the problem, but this topic is not discussed in this article.)


Prerequisites

We will continue the MySQL example from the previous article on how to create custom technology domains. If you have this example already running, you can skip this section and proceed with the next section. For all other readers, what follows is a "crash course" to get the MySQL domain running.

Import the custom MySQL domain

  1. Install the Topology Domain Generation Toolkit:
    In Rational Software Architect 7.5:
    1. Download the Topology Domain Generation Toolkit version 7.5 from the Downloads section and extract it to a temporary folder on your computer.
    2. Open IBM® Rational® Software Architect. Select Help > Software Updates and, in the Available Software tab of the wizard, click Add Site.
    3. Select Local and browse to the directory where you extracted the archive.
    4. Click OK and select Domain Generation Feature from the new update site in the Available Software dialog.
    5. Click Install and follow the installation procedure.
    In Rational Software Architect 8.0:
    1. Download the Topology Domain Generation Toolkit version 8.0 from the Downloads section, and extract it to a temporary folder on your computer. This file contains a feature that you must install into your Eclipse workbench.
    2. From the menu bar of the workbench, click Help > Install New Software.
    3. Click Add, click Local, and select the directory where you extracted the SDK.
    4. Give the repository a name, such as Domain SDK, and click OK.
    5. Clear the Group items by category check box.
    6. Select Domain Generation Feature and click Next.
    7. Using the wizard, complete the installation process and restart the workbench.
  2. Download the mysql.xsd file from the Downloads section of this article.
  3. Create a new project in Rational Software Architect:
    1. Select File > New > Project | General > Project, and then click Next.
    2. Enter org.example.mysql in the "Project name" field and click Finish.
  4. In the Package Explorer view, add a folder to the new project by right-clicking on the org.example.mysql project, selecting New > Folder, and specifying model as the name.
  5. Add two more folders under the new "model" folder and name them ecore and schema.
  6. Import the downloaded mysql.xsd file into your schema folder by copying the file on the operating system level into the project's workspace. In the Package Explorer, press F5 to see the file in your project.

Your project structure should look like the one shown in Figure 2.

Figure 2. Initial project structure for the MySQL technology domain extension
Package Explorer view of the project structure
  1. Create the ecore and genmodel files from the XSD file:
    1. Right-click the mysql.xsd file in the project, and click New > Other from the context menu.
    2. In Rational Software Architect version 7.5, click Eclipse Modeling Framework > EMF Model, and then click Next. In Rational Software Architect version 8.0, click Eclipse Modeling Framework > EMF Generator Model, and then click Next.
    3. Select the ecore directory for the genmodel file, and click Next.
    4. Select XML Schema as Model Importer and again click Next.
    5. Click Load to activate the mysql.xsd file and then click Next.
    6. On the next page of the wizard, select the org.example.mysql package on the upper area, and select all packages on the lower area of this dialog.
    7. Then click Finish.

If everything has gone right, the mysql.ecore and mysql.genmodel files are now generated in the ecore folder of your project.

Note: If you do not have a folder called Eclipse Modeling Framework in the New dialog window, enable the Show all Wizards check box.

  1. Generate the source code for the MySQL domain:
    1. Open the mysql.genmodel by double-clicking it in the Project Explorer view.
    2. Click the root element and open the Properties view (as shown in Figure 3).
    3. In the Properties view, set the Compliance Level attribute to 1.4 (as shown by item 1 in Figure 3) and the Non-NLS Markers attribute to true (item 2).
    4. Then, in the Edit Directory attribute value, delete .edit so that it is set to /org.example.mysql/src (item 3)
    5. Save the changes made to the .genmodel file (Ctrl+s).
    6. Again, select the root element of the .genmodel file and open the drop-down menu.
    7. From the drop-down menu, click Generate Model Code, and then Generate Edit Code, and, finally, a bit lower in the menu Generate Topology Edit Code.

Two plug-ins have been generated:

  • The model plug-in, which contains the model and the edit code
  • The plug-in org.example.mysql.ui, which contains the code for the diagram view
Figure 3. Edit the genmodel properties
genmodel - properties of the MySQL root element

Test the domain

  1. Test the domain in the runtime environment:
    1. Open one plugin.xml file either from the model or the UI project, and, in the Testing section in the Overview tab, click the Launch an Eclipse application link to start a runtime instance of Rational Software Architect.
    2. In the runtime instance, add a new project (File > New > Project | General > Project) and name it org.example.mysql.topology.
    3. Then, add a topology to the project (right-click on the new project in the Project Explorer New > Topology) and give it a name.
    4. From the Middleware drawer of the Palette, add a MySQLDatabaseSystemUnit to the topology.
    5. Close the runtime instance after you have tested your domain extension.

Elements of the validator API

When the Topology Edit Code is generated, the Topology Domain Generation Toolkit generates a single domain validator class for the technology domain and a unit validator class for each unit that is defined in the domain.

A domain validator contains logic that verifies that units are used properly in a topology. The domain validator creates instances of the unit validators for the domain, which validate the individual units in the domain, as we will discuss later. The domain validator can also include global validation logic for the entire domain. For example, it might include rules that apply to each unit in the domain or to many units in relation to each other. The domain validator might also include rules that ensure that values such as port numbers, host names, and IP addresses are unique within a topology.

A unit validator contains logic that validates a single type of unit and is broken into capability and attribute validators. For example, unit validators can verify that the attributes on capabilities on the unit are set to valid values and that the unit is linked to other units in a way that is appropriate for that type of unit. You can also add logic in the unit validators that pertains to specific capabilities, requirements, or attributes.

You can use these validators to extend the core validation rules that are provided in the topology. The default core validators ensure that the fundamental rules for topologies are met, such as the one that units with a hosting requirement have a hosting link. Adding your own logic to domain and unit validators lets you create rules that are specific to your domain.

The base package for the validators generated through the Topology Domain Generation Toolkit is namespace.internal.validator. Consequently, for this MySQL domain, as Figure 4 shows, there is one domain validator (MysqlDomainValidator) and one unit validator (MySQLDatabaseSystemUnitValidator) in the org.example.mysql.internal.validator package.

Figure 4. Package Explorer with generated validator classes
Shows the model projects class structure

Before you start to implement a custom validator, look at the architecture for how validators are defined in the model project,

  1. Open the plugin.xml file of the model project (org.example.mysql) and click the Extensions tab.
  2. Open the com.ibm.ccl.soa.deploy.core.domains extension and the entry labeled "Mysql domain." Under the Mysql domain node, you will find the entry for the domain validator as shown in Figure 5. It has only one attribute that specifies the validator class, which is automatically called for validation requests.
Figure 5. Validator declaration in the plugin.xml file
Extensions tab, selected validator entry

Larger view of Figure 5.

  1. To see the domain validator details, open the MysqlDomainValidator class and press F4 to open the Hierarchy Explorer of the class (see Figure 6).

The class specified by the domain validator extension must, directly or indirectly, extend the abstract DomainValidator class to be properly used by the validator framework. In this example, the generated domain validator, MysqlDomainValidator, extends UnitDomainValidator, which itself extends DomainValidator. To register a new unit validator, the addValidator(…) method must be called. If the validate(…) method of the UnitDomainValidator class is executed, then all registered unit validators are also called and the units are validated.

Figure 6. Class hierarchy of the DomainValidator
Type hierarchy view, UnitDomainValidator

Take a look at the generated source code of the MysqlDomainValidator (see Listing 1). The constructor method initializes its super class with the MySQL domain package. This notifies the validation framework that any unit declared by this package should be validated by this domain validator. Then, the internal addDomainValidators() method is called. Inside this method, all unit validators are initialized and registered to be included in the domain validator (here, the MySQLDatabaseSystemUnitValidator).

Listing 1. Generated source code of the MysqlDomainValidator
...
publicclass MysqlDomainValidator extends UnitDomainValidator {

   /**
    * Construct a {@link MysqlPackage} domain validator.
    * 
    * @generated (domain-generator)
    */
   public MysqlDomainValidator() {
      super(MysqlPackage.eINSTANCE);
      addDomainValidators();
 }

   /**
    * Add domain validators as needed.
    * 
    * @generated (domain-generator)
    */
   protected void addDomainValidators() {
      addValidator(new MySQLDatabaseSystemUnitValidator());
   }

...

}

Switch to the unit validators and take a look at the source code of the MySQLDatabaseSystemUnitValidator shown in Listing 2. This class is derived from UnitValidator, which serves as super class for all unit validators and contains methods to add more specialized validator classes, such as capability, attribute, and link validators. The MySQLDatabaseSystemUnitValidator contains two constructor methods and the methods addUnitTypeConstraints(…) and validateUnit(…). The method addUnitTypeConstraints(...) is called from the constructor and is where validations on the unit's contents are initialized, such as the types of capabilities and requirements that the unit can contain.

Listing 2. Generated source code of the MySQLDatabaseSystemUnitValidator
...
publicclass MySQLDatabaseSystemUnitValidator extends UnitValidator {

   public MySQLDatabaseSystemUnitValidator() {
      this(MysqlPackage.eINSTANCE.getMySQLDatabaseSystemUnit());
   }
...
   protected MySQLDatabaseSystemUnitValidator(EClass unitType) {
      super(unitType);
      assert MysqlPackage.eINSTANCE.getMySQLDatabaseSystemUnit().isSuperTypeOf(unitType);
      addUnitTypeConstraints();
   }
...
   protectedvoid addUnitTypeConstraints() {
      addCapabilityTypeConstraint(
         MysqlPackage.eINSTANCE.getMySQLDatabaseSystem(),
         CapabilityLinkTypes.ANY_LITERAL, 1, 1);
   }
...
   @Override
   publicvoid validateUnit(Unit unit, 
    IDeployValidationContext context, 
     IDeployReporter reporter) {
      super.validateUnit(unit, context, reporter);
   }
}

The method validateUnit(…) overrides the validateUnit(…) method from the UnitValidator class and is called when a unit instance is validated. This is the first entry point for your own unit validations. Be cautious to avoid expensive logic in this method due to how frequently it is called. In the next section, we'll add a unit validator that checks the MySQL version attribute. Then we will show how validation errors can be reported on the diagram level.


Add a simple unit validator method

The capability MySQLDatabaseSystem has an attribute named "MySQLVersion," with the xsd:string type. Thus, the default validators in the topology editor automatically ensure that the attribute has a string value. In this example, we want to go further to ensure that the version string always matches the typical structure of numbers and dots (for example: 4.04, 2.0.18, 5.23.42).

Update the unit validator class

In this section, you update the unit validator class to compare the MySQLVersion attribute to a regular expression. If the attribute does not match the version number format, the validator applies an informational message to the unit. The example gives you a quick overview of how the unit validator works and which steps are necessary to add a message to the diagram. This example is for demonstration purposes only. It could also be achieved by a DeployAttributeValidator, if required. DeployAttributeValidators are introduced later in this article.

The first thing that you must do is to add the org.eclipse.core.resources package to the plug-in dependencies of the org.example.mysql plug-in. This package contains basic validation and error reporting mechanisms from the Eclipse platform. They are leveraged in the topology editor's validation framework.

  1. Open the plugin.xml file in the Plug-in Manifest Editor, and select the Dependencies tab.
  2. In the Required Plug-ins area, click Add and enter org.eclipse.core.resources in the text field, select the package in the dialog box and click OK.
  3. Save the changes that you have made to the plugin.xml file.

In the next step, you will edit the validateUnit(…) method of the MySQLDatabaseSystemUnitValidator class, which is called when the unit is validated. The validateUnit(…) method gets the following input parameters:

Unit unit
This is the current object that is validated. This object allows you to get access to the current values of attributes and linkages to other objects.

IDeployValidationContext context
This is the extended system environment for the validation.

IDeployReporter reporter
This interface enables you to add status messages to the editor.

  1. Replace the existing validateUnit(…) method in the MySQLDatabaseSystemUnitValidator class with the method from Listing 3 and add the global variable MYSQL_DB_VERSION_VALIDATOR_ID.
  2. Press Ctrl+Shift+o to add the missing import declarations and fix the errors in the source code.
  3. To make sure that your own source code is not overridden by a new generator run, delete the @generated annotation of the validateUnit(…) method.
Listing 3. Adding a first validation to the MySQLDatabaseSystemUnitValidator class
...
publicstatic final String MYSQL_DB_VERSION_VALIDATOR_ID =
      "MYSQL_DB_VERSION_VALIDATOR_ID";
...

publicvoid validateUnit(Unit unit, IDeployValidationContext context,
     IDeployReporter reporter) {
   super.validateUnit(unit, context, reporter);
   // get the MySQLDatabaseSystem capability from the current unit
   MySQLDatabaseSystem mysqlDBSystem = 
      (MySQLDatabaseSystem)ValidatorUtils.getCapability(unit,
           MysqlPackage.eINSTANCE.getMySQLDatabaseSystem());
   String version = mysqlDBSystem.getMySQLVersion();
   // check the version string against the regular expression
   if (version != null && !Pattern.matches("([0-9]+\\.)+[0-9]+", version)) {
      // if the version check fails,
      // create a new deploy status object with a status message
      IDeployStatus status = 
          DeployCoreStatusFactory.INSTANCE.createDeployStatus(
             IStatus.INFO,
             MySQLDatabaseSystemUnitValidator.MYSQL_DB_VERSION_VALIDATOR_ID,
             "InvalidVersionNumber",
             "Version number \"{0}\" does not match X.Y.Z",
             new Object[]{version},
             unit);
      // add the new status to the reporter
      reporter.addStatus(status);
   }
}

The new validateUnit method in the validator class does the following things to determine whether the unit is valid and to report problems with the unit:

  1. The validator calls the validateUnit method(...) in its supertype: super.validateUnit(unit, context, reporter);
    Any validation errors reported by the parent validator will be accumulated with those discovered by this validator.
  2. It retrieves the MySQLDatabaseSystem capability from the unit with ValidatorUtils.getCapability.MySQLDatabaseSystem mysqlDBSystem =
    (MySQLDatabaseSystem)ValidatorUtils.getCapability(
    unit,MysqlPackage.eINSTANCE.getMySQLDatabaseSystem());
  3. It retrieves the version attribute in string form.
    String version = mysqlDBSystem.getMySQLVersion();
  4. In the if clause, the version string is first checked against null (version != null). If it is not null, the regular expression (Pattern.matches("([0-9]+\\.)+[0-9]+", version)) evaluates whether the string matches the predefined pattern.
  5. If the pattern does not match the version string pattern, a status message is created:
    DeployCoreStatusFactory.INSTANCE.createDeployStatus(…)
  6. The last step for this validation is to add the new status message to the reporter, which adds the message to the editor:
    reporter.addStatus(status);

In Step 5, we used the DeployCoreStatusFactory class that contains helpful methods to create different kinds of status objects (in this case, an IDeployStatus). We passed the following parameters to the createDeployStatus(…) method:

  1. int severity: The severity codes are encapsulated in the IStatus interface and can have the values IStatus.INFO (for informative messages), IStatus.WARNING (for warning messages) and IStatus.ERROR (for error messages). Depending on the severity, a different symbol is shown for the message (a blue circle, a yellow triangle, or a red circle with a cross inside).
  2. String validatorId: Each validator has its own system wide unique identifier as string representation. In our example, the identifier is stored in a static class variable so that it can be accessed from outside if necessary.
  3. String problemType: An identifier that classifies the problem and that is internally consumed in resolution mechanisms.
  4. String unboundMessage: The message to be shown in the diagram.
  5. Object[] bindings: Array used for the Native Language Support (NLS) bindings for the error message. In this example, the current value of the version string is passed to the unboundMessage string. The value is shown instead of the placeholder {0} in the unboundMessage.
  6. DeployModelObject object: The model object that is affected. In this example, we added the status information directly to the unit.

Test the validator

Now you can test the validator:

  1. When you have updated the validator method, save the file (Ctrl+s) and launch a runtime environment.
  2. Create a new MySQLDatabaseSystemUnit from the Middleware palette drawer, and open the Capabilities tab of the Properties View.
  3. Enter a caption for the <no productName> capability (for this exercise, use MySQL Database System), and scroll down to the My SQL Version attribute.
  4. Enter an invalid word, such as test, and put your mouse cursor directly on the unit's status information (the blue circle with a capital letter I in the MySQLDatabaseSystemUnit), as Figure 7 shows. There are two messages shown:
    • The first is our message indicating the invalid version number
    • The second is a default optional hosting message for new units
Figure 7. Version check failed for the MySQLDatabaseSystemUnit
Topology editor with failed version check

Larger view of Figure 7.

Note:
You can also access capabilities and requirements directly by double-clicking the unit and going to the Capabilities page (see Figure 7).

  1. Now, set the My SQL Version attribute to a typical version number, such as 5.1, and check the status information again. Only the optional hosting message should appear.
  2. Save the diagram, and close the runtime environment.

This example shows a simple way to validate a unit. You can add more code to the validateUnit(...) method to validate other attributes on the unit. You can also perform more complicated validations, such as verifying that this version number is compatible with the version number of a different unit. The rest of this article discusses other techniques for validating units.


Reuse existing attribute validators

Besides the option to write all of your validations yourself, you can use different existing validators that are provided by the core validation framework. Using existing validators enables reuse and makes adding simple validations to your custom domain easy. A few simple examples are validators that determine whether an attribute value is set or whether an attribute has a specific value.

In this section, you will add two existing AttributeValidators to your MySQLDatabaseSystemUnitValidator class that report a warning message (a) if the MySQLVersion string is empty and (b) if the port attribute has the value of 8080, which is used in application servers such as Apache Tomcat as a default value and is thus not a good choice for the MySQL port.

  1. Create a new method called addMyAttributeValidators() in the MySQLDatabaseSystemUnitValidator class, and call the method from the constructor method as shown in Listing 4.
Listing 4. Method skeleton for additional DeployAttributeValidators
..
protected MySQLDatabaseSystemUnitValidator(EClass unitType) {
...
   // call the new method
   addMyAttributeValidators();
}

protectedvoid addMyAttributeValidators() {
 // register your AttributeValidators here
...
}

You need to assign a unique string identifier to each predefined validator that you use, as shown in the next steps:

  1. Add the two class variables and the source code of the two attribute validator calls from Listing 5. Again, you can copy and paste the contents of the addMyAttributeValidators() method and fix the imports again by pressing Ctrl+Shift+o.
  2. Save the changes to the class.
Listing 5. Code snippets to initialize existing DeployAttributeValidators
...

publicfinalstatic String VERSION_NOT_EMPTY_ID = "VERSION_NOT_EMPTY_ID";
publicfinalstatic String PORT_NOT_8080_ID = "PORT_NOT_8080_ID";

...

protectedvoid addMyAttributeValidators() {
   // a. Warning if the version string is empty
   addAttributeValidator(new DeployAttributeStringNotEmptyValidator(
      MySQLDatabaseSystemUnitValidator.VERSION_NOT_EMPTY_ID,
      MysqlPackage.Literals.MY_SQL_DATABASE_SYSTEM__MY_SQL_VERSION,
      IStatus.WARNING));
   // b. Check that the port is not 8080
   addAttributeValidator(new DeployAttributeNotEqualsValidator(
      MySQLDatabaseSystemUnitValidator.PORT_NOT_8080_ID,
      MysqlPackage.Literals.MY_SQL_DATABASE_SYSTEM__PORT,
      new BigInteger("8080"),
      IStatus.WARNING));
}

This code instantiates two attribute validators in the unit validator and registers them with the addAttributeValidator(…) method. Thus, the attribute validators will be called each time the unit is validated.

The DeployAttributeStringNotEmptyValidator takes three input parameters:

  1. String validatorID: The unique identifier of the validator. In our example, we refer to the VERSION_NOT_EMPTY_ID declared class attribute.
  2. EAttribute attribute: The attribute on which this validator should operate. This parameter refers to the EMF structural feature (EAttribute) that can be accessed from the EMF domain package.
  3. int severity: This attribute is the severity of the message, which is again used from the IStatus interface.

The DeployAttributeNotEqualsValidator accepts the same parameters, plus either one invalid value (as shown in Listing 5) or an array of invalid values. If the attribute's value matches one of the invalid values, the warning message is shown.

Note:
You must use an appropriate validator for the attribute type. The DeployAttributeStringNotEmpty validator works only on string attributes or attributes with types that extend the primitive type xsd:string. The DeployAttributeNotNull validator works on complex data types such as the port (os:port) and checks that they have a value.

After you have edited the MySQLDatabaseSystemUnitValidator class, you can test the changes in the runtime environment. If the MySQLVersion attribute has no value, a warning message appears on the unit as shown in Figure 8. If you set the port attribute's value to 8080, the "port value 8080 is invalid" warning is shown in the diagram. Close the runtime environment after your tests.

Figure 8. Warning messages from existing attribute validators
Screen capture of warning message

Use other predefined validators

There are several other predefined validators that work in a similar way. For example:

  • DeployAttributeInvalidPathnameValidator ensures that file and directory paths are appropriate for an operating system.
  • DeployAttributeRestrictedValueValidator restricts input fields to predefined values.

You can get a list of all attribute validators by clicking on an attribute validator in the source code, then open the type hierarchy explorer (F4) and select the DeployAttributeValidator class. Open the context menu and select Focus On 'DeployAttributeValidator' and have a look at the displayed subclasses.


Create your own DeployAttributeValidator

If you have complex validations, or if you want to reuse your validation source code, you can create your own attribute validator by subclassing the DeployAttributeValidator class and implementing the validate(…) method. In this section, you create your own attribute validator class, which verifies that an attribute value is a multiple of 1024. The validator is then used to check the MAX_ALLOWED_PACKET attribute, due to the MySQL recommendation of the attribute's value.

  1. Click the org.example.mysql.internal.validator package in the Project Explorer view, open the drop-down menu, and select New > Class.
  2. Enter the class name as MyAttributeValidator.
  3. Next to the Superclass field, click Browse to specify the super class of the new validator.
  4. In the dialog window that follows, type DeployAttributeValidator, select the matching class, and click OK.
  5. Select the Constructors from superclass check box and click Finish.

The class skeleton is generated and you can now edit the validate method:

  1. In the MyAttributeValidator class, replace the validate(…) method with the validate method from Listing 6, and add the createNotNullOrZeroStatus(…) helper method, which is also included in Listing 6.
  2. Fix all imports (Ctrl+Shift+o) and save the changes.
Listing 6. validate(…) method for your own DeployAttributeValidator
...
@Override
publicvoid validate(DeployModelObject object,
 IDeployValidationContext context, IDeployReporter reporter) {
   // get the value of the attribute from the super class
   Object value = super.getAttributeValue(object);
   // check that the object is neither null nor zero
   if (value != null) {
      // convert the value into int
      int val = ((Integer) value).intValue();
      if (val != 0) {
         // test if multiple of 1024
         if (val % 1024 != 0) {
             // if not, create a status and add it to the reporter
             reporter.addStatus(DeployCoreStatusFactory.INSTANCE
                .createDeployStatus(
                    IStatus.ERROR,
                    super.getValidatorID(),
                    "MaxAllowedPackageMustBeAMultipleOf1024",
                    "Value of attribute MaxAllowedPackage, \"{0}\" should be a " + 
                    "multiple of 1024",
                    new Object[] {value},
                    object));
         }
      } else {
        this.createNotNullOrZeroStatus(object, context, reporter);
      }
   } else {
     this.createNotNullOrZeroStatus(object, context, reporter);
   }
}

privatevoid createNotNullOrZeroStatus(DeployModelObject object, 
      IDeployValidationContext context, IDeployReporter reporter) {
   reporter.addStatus(
      DeployCoreStatusFactory.INSTANCE.createDeployStatus(
          IStatus.ERROR,
          super.getValidatorID(),
          "AttributeNullOrZero",
          "Max Allowed Package should not be null or zero",
          new Object[] {},
          object));
}
  1. Finally, instantiate the new validator in your MySQLDatabaseSystemUnitValidator class, as shown in Listing 7, and save the changes.
Listing 7. Adding the new validator to the Unit Validator class
...
publicfinalstatic String MAX_ALLOW_PACKAGE_VALIDATOR_ID = 
       "MAX_ALLOW_PACKAGE_VALIDATOR_ID";
...
protectedvoid addMyAttributeValidators() {
...
   // c. Check that max_allowed_packet is multiple of 1024 
   addAttributeValidator(
      new MyAttributeValidator(
        MySQLDatabaseSystemUnitValidator.MAX_ALLOW_PACKAGE_VALIDATOR_ID,
        MysqlPackage.Literals.MY_SQL_DATABASE_SYSTEM__MAX_ALLOWED_PACKET));
...
}
...

The new MyAttributeValidator class consists of one constructor method, the inherited validate(…) method, and the createNotNullOrZeroStatus(…)helper method. The constructor takes two input parameters, which are passed to constructor method of the super class (super(validatorID, attribute);):

  1. String validatorID: The unique identifier
  2. EAttribute attribute: The attribute on which the validator operates

The input parameters of the validate(…) method are the same as the input parameters of the validateUnit(…) method of the Add a simple unit validator method section The validate(…) method contains the following operations:

  1. The attribute's value is retrieved:
    (Object value = super.getAttributeValue(object);)
  2. The value is checked against null.
  3. If the value is not null, it is converted into an int value:
    (int val = ((Integer) value).intValue();)
  4. Then the value is checked against zero.
  5. If the value is not zero, value is checked to see if it is a multiple of 1024. If it is not, the error message is created.

If the attribute's value is null or zero, the createNotNullOrZeroStatus(…) helper method is called. This code is separated into a different method to avoid duplicating the code.

Again, you can test the changes in the runtime environment. If you enter a value that is a multiple of 1024, the error message is not shown anymore.

This attribute validator contains a very simple algorithm; your validators can be as complex as necessary. The goal of this section was to show how you can benefit from the existing API and how you can create your own validator library. There are numerous use cases surrounding attribute validation. With your own validator classes, you can reuse your algorithms in different projects and technology extensions.

In the next section, we will leave the attribute validators and demonstrate how you can validate links between units.


Check links and types with a domain validator

Each unit can have different relationships with and dependencies on other units. Sometimes, you might want to validate these relationships and report a status message about them. In our MySQL example, each MySQLDabaseSystemUnit should host at least one DatabaseInstanceUnit, which is the corresponding unit in the database domain that stores application data. In this section, you will add this validation rule to the validateUnit(…) method of the MySQLDatabaseSystemUnitValidator.

  1. Open the MySQLDatabaseSystemUnitValidator class and add a new class attribute for the validator identifier to your source code (see Listing 8).
  2. Scroll to the validateUnit(…) method and add the source code from Listing 8 just behind the code that checks the version number.
  3. Fix the imports by pressing Ctrl+Shift+o. When you are prompted to select a class to represent the List object, select the java.util.List interface.
Listing 8. Code snippet to check that at least one DatabaseInstanceUnit is hosted in the MySQLDatabaseSystem
...
publicfinalstatic String MY_SQL_DATABASE_SYSTEM_HOSTING_WARNING_ID = 
       "MY_SQL_DATABASE_SYSTEM_HOSTING_WARNING_ID";
...
publicvoid validateUnit(Unit unit, 
       IDeployValidationContext context, IDeployReporter reporter) {
   super.validateUnit(unit, context, reporter);
   // version check
...
   // hosting check
   // get all hosted units inside the MySQLDBUnit
   // that are the type of DATABASE_INSTANCE_UNIT
   List<Unit> hostedInstances = 
      ValidatorUtils.getHosted(unit,
         DatabasePackage.Literals.DATABASE_INSTANCE_UNIT);
   if (hostedInstances.size() < 1) {
      IDeployStatus stat = 
          DeployCoreStatusFactory.INSTANCE.createDeployStatus(
             IStatus.WARNING,
             MySQLDatabaseSystemUnitValidator.MY_SQL_DATABASE_SYSTEM_HOSTING_WARNING_ID,
             "MissingDatabaseInstanceUnitOnMySQLDatabaseSystem",
             "At least one DatabaseInstanceUnit should be hosted on MySQLDatabaseSystem.",
             new Object[] {},
             unit);
      reporter.addStatus(stat);
   }
}

This validator uses the getHosted(…) method from the ValidatorUtils class to retrieve a list of units that the specified unit hosts. This example uses the following input parameters:

  1. Unit unit: The current unit, which, in this case, is the MySQLDatabaseSystem unit that is being validated.
  2. EClass hostedUnitType: Optionally, you can restrict the list of hosted elements to a specific unit type. In this case, the returned list should contain only DatabaseInstanceUnits. Therefore, we use the DatabaseInstanceUnit type from the LITERALS interface from the DatabasePackage interface of the database technology extension.

If the returned list of DatabaseInstanceUnits is empty, which implies that the MySQLDatabaseSystem unit does not host DatabaseInstanceUnits, the error message is shown.

  1. Check the result in your runtime environment.

Note:
You can add a new DatabaseInstanceUnit quickly by pressing Ctrl+t in the diagram and then entering Database Instance. If you have not added the DatabaseInstanceUnit to the MySQL unit, you will see the error message in the diagram. Figure 9 is a collage of two MySQLDatabaseSystemUnits and their respective status messages, one with and the other without a DatabaseInstanceUnit.

The ValidatorUtils class contains many helpful methods to access the structural elements of a unit (including capabilities, requirements, constraints) and to retrieve information about the unit's relationships. In most cases you can refer to one of the static methods of the ValidatorUtils class and need not muddle through the generated EMF API of the unit itself.

Figure 9. The hosting validation demonstrated on two MySQLDatabaseSystemUnits
Status messages for two MySQLDatabaseSystemUnits

Validate the topology with the Domain Validator class

So far, we have demonstrated different kinds of unit and attribute validators. In this section, we show how to update the domain validator class to validate a set of units in a diagram. To continue our example, think of a situation where two MySQL database systems should be installed on one operating system (in a test environment, for instance). Each MySQL database system communicates over a port with other applications. In this example, we must ensure that each database system uses its own port.

As a first step, you will create the test environment for this example in the runtime environment. Optionally, you can download the DomainValidatorRuntimeExample example from the Downloads section and import it into your runtime workspace.

  1. Open the runtime environment and create a new topology.
  2. Add two MySQLDatabaseSystem units to the diagram, and resolve each warning from the previous examples.
  3. Add an operating system (for example, a SUSE Linux Server 9) and an x86 server (from the hardware drawer) to the diagram.
  4. Set the root password for the operating system, and host the operating system on the hardware.
  5. Host both MySQLDatabaseSystemUnits on the operating system.
  6. Add the Port Consumer capability to the operating system. The capability can be found if you enter os.PortConsumer into the text field of the Add Capability dialog window.
  7. Add a Port Configuration unit from the Configurations drawer to the diagram, and give the port a name.
  8. Host the port configuration on the operating system.
  9. Add a new requirement to both MySQLDatabaseSystemUnits, set the Caption to port configuration and the Type to os.Port.

All other parameters can be left in their default configurations. If you have finished the configuration, each MySQLDatabaseSystemUnit shows the warning "Unsatisfied 'port configuration' requirement."

  1. Resolve these warnings by creating a dependency link from each MySQLDatabaseSystemUnit to the port configuration unit on the operating system. These links resolve the errors in the diagram, but this arrangement would not work in a real system due to the multiple port usages. Thus, a validator must point out this problem.

Figure 10 shows the complete example of the port requirement in the DatabaseB unit.

Figure 10. Initial topology for the domain validator example
2 MySQLDatabaseSystemUnits that depend on 1 port

Larger view of Figure 10.

To show an error message on the configuration shown in Figure 10, you could add a validator to the PortConfiguration unit that reports multiple incoming dependency links from the MySQLDatabaseSystemUnits. A better way is to place the validation logic in the domain validator class of the MySQL domain (MysqlDomainValidator).

The validation check for this example is shown in Listing 9. Again, you can copy and paste the method into your MysqlDomainValidator class, fix the imports (Ctrl+Shift+o), and select java.util.Iterator and java.util.List as imported classes.

Listing 9. Code snippet to check the port usage in the MysqlDomainValidator class
... 
@Override
publicvoid validate(IDeployValidationContext context, IDeployReporter reporter) {
   // call validator method of super class
   super.validate(context, reporter);
   // get all MySQLDatabaseSystemUnits
   Iterator<MySQLDatabaseSystemUnit> it = 
	context.findAllUnits(MysqlPackage.Literals.MY_SQL_DATABASE_SYSTEM_UNIT);
   String message = "Multiple Port Usage: \"{0}\"";
   Set<PortConfigUnit> portConfigs = new HashSet<PortConfigUnit>();
   // iterate over the the units
   while (it.hasNext()) {
      MySQLDatabaseSystemUnit mysqlDBSystem = it.next();
      // get all requirements of the mysql unit
      List<Requirement> reqs = mysqlDBSystem.getRequirements();
      for (Requirement req : reqs) {
         // get the link target of each requirement
         Capability cap = ValidatorUtils.getDependencyLinkTarget(req);
         // check if link target is PortConfigUnit type
         if (cap != null && ValidatorUtils.getUnit(cap) instanceof PortConfigUnit) {
            PortConfigUnit portConfigUnit = (PortConfigUnit) cap.getParent();
            // check if PortConfiguration is in use by other Mysql DB Systems
            if (portConfigs.contains(portConfigUnit)) {
               // if yes: create an Error message
               IDeployStatus status = 
                  DeployCoreStatusFactory.INSTANCE.createDeployStatus(IStatus.ERROR,
                     "DomainValidID",
                     "MultiplePortUsage",
                     message,
                     new Object[] { portConfigUnit.getDisplayName()},
                     portConfigUnit);
               reporter.addStatus(status);
            } else {
              // if not: add portConfigUnit to used PortConfigurations
              portConfigs.add(portConfigUnit);
            }
         }
      }
   }
}
...

You can implement the validation check for this example with these steps:

  1. The validate(…) method of the super class is called to ensure that all registered validators are executed.
  2. From the context object that is passed to the validate(…) method, all MySQLDatabaseSystemUnits are retrieved with this code:
    context.findAllUnits(…). The method is called with one input parameter that specifies the type of the elements that you want to retrieve. As in other validators in this article, the type can be retrieved from the LITERALS interface.
  3. For each MySQLDatabaseUnit in the list, the requirements are retrieved and stored in a list:
    List<Requirement> reqs = mysqlDBSystem.getRequirements();
  4. In the next step, the target of each link is retrieved:
    Capability cap = ValidatorUtils.getDependencyLinkTarget(req);
  5. The capability is checked against null in the first part of the if clause.
  6. The unit that contains the capability is retrieved (cap.getParent()) and checked if it is a PortConfigUnit. (Keep in mind that a dependency requirement is always connected to a capability of a unit, not to the unit itself. Thus, you need to retrieve the parent of the capability to examine the unit.)
  7. If the unit is a PortConfigUnit, check that the unit is not already listed in a set (list of unique elements). If the unit does not exist, put it into the set. If it exists in the set, then the PortConfigUnit is still in use and an error message is created.

Figure 11 shows the resulting error message.

Figure 11. Error message from the DomainValidator
One IP_Interface requirement must be declared

As you can see, you can add complex validations that affect the whole topology with all of its units. You can add as much code as you need to validate the domain, but remember to call the validator method of the super class to ensure that the topology editor can complete all of the necessary validations.


Create a validation project

The previous examples in this tutorial showed how to add validators directly to the custom technology domain and its generated source code. In this section, we will cover how to create validators in a separate project, which might be useful in the following cases:

  • You want to add a validator to an existing model element (the core unit type), but the source code is not accessible.
  • To ease source code maintenance, the validation source code should be separated from the source code of the technology domain.
  • The new validators can be re-used in different custom technology domains (if you have written a generic AttributeValidator, for example).

As we continue our example, we want to ensure that each PortConfigUnit has access to an IPInterfaceUnit, which handles the network communication. By default, the only requirement of a PortConfigUnit is a hosting requirement for a PortConsumer. In this example, we want to create an error message if the PortConfigUnit is not equipped with a second requirement for a dependency link to an IPInterface (see Figure 12).

Figure 12. PortConfigUnits with error message (upper unit) and with desired requirement (lower unit)
2 PortConfigUnits with different configurations
  1. Create a new plug-in project:
    1. Select File > New > Project > Plug-in Development > Plug-in Project and then click Next.
    2. Name the new project org.example.mysql.validation, and proceed with the wizard by clicking Next.
    3. Leave all settings of the new project on the default parameters, and click Finish.

The wizard automatically creates the plug-in skeleton and opens the plug-in editor in the workspace area.

  1. Open the Dependencies tab of the plug-in editor and add the following plug-ins to the Required Plug-ins section:
    1. The resources plug-in, org.eclipse.core.resources, to get access to the Eclipse API.
    2. The used plug-ins from the topology editor:
      • The core API: com.ibm.ccl.soa.deploy.core
      • The plug-in for the Network API to access the IPInterface declaration:
        com.ibm.ccl.soa.deploy.net
      • The plug-in for the operating system API to access the PortConfigUnit:
        com.ibm.ccl.soa.deploy.os

Note:
To accelerate the package selection in the Add options, you can enter the plug-in names in the text field at the top of the dialog window. The selection is then filtered to matching plug-ins. Here, you can also use placeholders, such as the asterisk. For example, if you want to select the OS package you can type *.deploy.os.

  1. Save the changes made in the editor by using Ctrl+s.

The next step is to register the validator with the plug-in.

  1. Open the Extensions tab of the plug-in editor.
  2. Click Add and select the com.ibm.ccl.soa.deploy.core.domains extension point,
  3. Click Finish.
  4. Back in the plug-in editor, edit the Extension Element Details form, and set the following values:
    • Name: MyDomain
    • Id: org.example.mysql.validation
    • targetNamespace: http://www.ibm.com/ccl/soa/deploy/os/1.0.0/
  5. Then add a validator to the extension by right-clicking the domain entry under com.ibm.ccl.soa.deploy.core.domains and selecting New > validator, as shown in Figure 13.
Figure 13. Add a validator to the domain
Drop-down menus in the plugin.xml Extensions tab
  1. In the Extension Element Details form on the right side of the plug-in editor, you can now select an existing validator class or create a new validator class (as in this case).
  2. Click the class* link, which opens the New Java Class dialog window.
  3. Enter PortConfigDomainValidator as class name.
  4. In the Superclass field, specify:
    com.ibm.ccl.soa.deploy.core.validator.pattern.UnitDomainValidator
  5. Select the check boxes for Constructors from superclass and Inherited abstract methods, and click Finish.

The new validator class is now generated and opened in the workspace. You can now edit the contents of the new validator with your own source code.

  1. Add the constructor method and the validate(…) method from Listing 10 to the validator class.
  2. Fix the imports (Ctrl+Shift+o) and save the changes (Ctrl+s).
Listing 10. Code snippets to complete the PortConfigDomainValidator
... 

public PortConfigDomainValidator() {
   super(OsPackage.eINSTANCE);
}

@Override
publicvoid validate(IDeployValidationContext context, IDeployReporter reporter) {
   // call validator method of super class
   super.validate(context, reporter);
   // get all PortConfigUnits of the topology
   Iterator<PortConfigUnit> it = 
	context.findAllUnits(OsPackage.Literals.PORT_CONFIG_UNIT);
   // iterate through all PortConfigUnits
   while (it.hasNext()) {
      PortConfigUnit portConfigUnit = it.next();
      // from each PortConfigUnit get all requirements of type IP_Interface
      List<Requirement> requirements = 
         ValidatorUtils.getRequirements(
         portConfigUnit, 
         NetPackage.Literals.IP_INTERFACE);
      if (requirements != null && requirements.size() != 1) {
         // if there is no or there are too many IP_Interfaces, report an error
         reporter.addStatus(
            DeployCoreStatusFactory.INSTANCE.createDeployStatus(
              IStatus.ERROR,
              "PORT_CONFIG_DOMAIN_VALIDATOR_ID",
              "MissingIPInterfaceRequirement",
              "Exactly one IP_Interface requirement has to be declared.",
              new Object[] {}, portConfigUnit));
      }
   }
}

The validator plug-in uses Eclipse's extension point mechanism to register the new functionality.

In Step 5, you selected the com.ibm.ccl.soa.deploy.core.domains extension point, which offers the ability to add a new domain validator. This domain validator is related to a specific domain, which is configured with a target namespace. Because the PortConfiguration unit is located in the operating system domain, we set the corresponding targetNamespace attribute for that domain (http://www.ibm.com/ccl/soa/deploy/os/1.0.0/).

Then you created a new class that is derived from UnitDomainValidator, where you added a new constructor method and the validate(…) method from Listing 10. In the constructor method, you registered the operating system domain as target domain (OsPackage.eINSTANCE). The validate(…) method contains the following instructions:

  1. The validate method of the super class is called: super.validate(...)
  2. All PortConfigUnits of the current topology are retrieved with the findAllUnits(…) method of the context object. Again, from the LITERALS interface, the requested type is passed as input parameter to the method.
  3. The return type of the findAllInstances(…) method is a java.util.Iterator that points to a collection of PortConfigUnits.
  4. For each PortConfigUnit in the collection, the requirements are determined. Again, the ValidatorUtils class and the method getRequirements(…) is called with the following attributes:
    1. Unit unit: The current unit to be validated. In this case, the current PortConfigUnit is passed to the method.
    2. EClass type: The type of requirement that should be retrieved. Because we want to check that exactly one net.IpInterface requirement is configured, we retrieve the corresponding EMF class from the NetPackage's Literals interface:
      NetPackage.Literals.IP_INTERFACE
  5. The return type of the getRequirements(…) method is a java.util.List that can be checked to see whether it contains exactly one element. If it does not, the error message is created and added to the reporter.

You can test the new validator in the runtime environment. If you add a new PortConfigUnit to a diagram, you should now see the same error message as the one shown in Figure 12. If you create a new requirement and set the type to net.IpInterface, the error message disappears.

With a separate validator project, you can add as many validators as you want to your own or to existing technology domains in the topology editor. The entry point is always the com.ibm.ccl.soa.deploy.core.domains extension point, which lets you register a new domain validator class. From this domain validator class, you can instantiate and register your own unit and attribute validators.


Summary

In this tutorial, we showed how validation mechanisms can greatly enhance your technology domain extensions at different levels of abstraction. You can add validators on attributes, units, capabilities, and domains, and you can report the status to the model user as an informational message, warning, or error message. Custom validators can contain detailed logic to examine the elements of a topology through the API, such as the static methods of the ValidatorUtils class. In most cases, it will be sufficient for you to edit and override the existing validate(…) methods. For more complex validations, you can also use the API provided with the deployment planning tools in Rational Software Architect to add your own validator project with its own set of rules.

Having validators in a technology domain extension increases the value of that domain to modelers. The next step to enhance your custom domains might be to add resolution mechanisms to resolve to the problems discovered by the validators in your custom domain.


Downloads

DescriptionNameSize
The MySQL XML schema definitionmysql.zip1KB
Source code from all projects for version 7.5dW-Validation-Mechanisms-Topology-Editor-Source-75x.zip106KB
Source code from all projects for version 8.0dW-Validation-Mechanisms-Topology-Editor-Source-80.zip106KB
Sample MySQL topologiesdW-Validation-Mechanisms-Topology-Editor-runtime-example.zip6KB
Topology Generation Toolkit for version 7.5Zephyr-SDK-75x.zip151KB
Topology Generation Toolkit for version 8.0Zephyr-SDK-80.zip151KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Rational software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational
ArticleID=630638
ArticleTitle=Use the topology editor in Rational Software Architect to add a custom validator
publish-date=08232011