Walk the skyway: Generating Spring applications with Rational and the Skyway Builder

Spring development, even faster!

This article describes how to generate spring applications from IBM® Rational® Software Architect models, how to adjust code generation templates. and how to integrate existing 'handmade' code. Also, it describes how to create the physical data model from the class model using IBM® InfoSphere™ Data Architect. Although the Spring Framework made your life as a developer much easier, it is still tedious to develop huge applications. Code generation is the solution to eliminate repetitive work and to accelerate development. This article shows you how to start from the pole position to generate a Spring-based working CRUD (create, read, update, and delete) application with the Skyway Builder. The generation is based on a domain UML class model created with Rational Software Architect. Likewise, the UML class model is the starting point for the transformation to a physical data model (PDM) using InfoSphere Data Architect. With the PDM, the database tables are created. Then the Code Generation JET (Java™ Emitter Template) for the entities is adjusted, demonstrating the flexibility of the Skyway Builder. Furthermore, the handcrafted heritage code is integrated into the generated application. Finally, Skyway´s Visual Builder capability is demonstrated when you model a two-step Spring webflow example. As a result, you create a well-designed Spring certified application, which is a simplified example of a JMX (Java Management Extension) enabled Application-Management-Tool.

Dirk Lammers (dirk.lammers@de.ibm.com), IT Architect, IBM Global Business Services

photo of Dirk LammersDirk Lammers is an accredited IT architect who has more than 15 years of experience as a professional software developer. He belongs to Application Innovation Services, a Service Line of IBM GBS, and works full-time as a lead architect in a long-running customer project.



20 April 2010

Also available in Chinese Portuguese

Frequently used acronyms

  • MDSD: Model Driven Software Development
  • EMF: Eclipse Modeling Framework
  • JET: Java Emitter Templates
  • DSL: Domain-specific language
  • CRUD: Create, Read, Update, Delete
  • SWF: Spring Web Flow
  • LDM: Logical Data Model
  • PDM: Physical Data Model
  • DDL: Data Definition Language
  • JMX: Java Management Extensions
  • JPA: Java Persistence API
  • DAO: Data Access Object
  • AOP: Aspect Oriented Programming

Imagine that you join a new project and the day that you arrive you are faced with a wallpaper of one hundred entities or more. Your mission is to create a UML model for the business logic, and to code the application. Certainly the business logic is based on creating, reading, updating, and deleting the data represented by the entities. Of course coding the application is not as hard as in former times, when AOP and Spring did not exist. Much of the boilerplate code has gone, due to concepts like Dependency Injection, Annotations, and Aspect Orientated Programming. Therefore, the use of the de facto standard Spring is a requirement in your new project. Though you are very happy about this requirement, you are not looking forward to implementing, for example, all finder queries for each Entity attribute, along with uncountable access methods through all layers of a Java™ Enterprise Edition (JEE) application. This will definitely take a very long time, and moreover it is very tedious.

Now, you may see a glimmer of hope. Choosing the right tooling composition gives you a jump start, and you can immediately concentrate on your business logic. Through the integration of IBM® InfoSphere™ Data Architect, IBM® Rational® Software Architect, and the Skyway Builder, as shown in Figure 1, you get a powerful set of tooling that enables you to industrialize your JEE development.

Figure 1. Tool composition
Tool Composition: Infosphere Data Architect, Rational Software Architect and the Skyway Builder plug-in

Using this tool composition you have separated models, which can be transformed from and to each other. A data model can be transformed to an application model, and the application model can be transformed to Skyway´s Spring DSL. Spring DSL is Skyway´s domain specific language for enterprise applications. Nowadays, it is best practice to generate code from a DSL, which is simpler to use than a general purpose language like UML. Spring DSL is a formalized set of EMF models. The Skyway Builder is a code generation and scaffolding tool for accelerating the development of Spring Web applications. The Skyway Generation Engine follows best practices of MDSD. For example, the generated code is separated from the handcrafted code. The project containing the DSL model is separated from the Web project generated by the engine. Therefore, you are never stuck on the model, if you decide to proceed without generation. Because of the fact that EMF is the core of the Skyway Builder, its Generation Engine is very flexible and highly extensible.

There are five editions available, from open-source to commercial.

  • Community
  • Standard
  • Web Services
  • Professional
  • Rational Software Architect

The Rational Software Architect Edition integrates with Rational Software Architect, and it supports the transformations of UML into Spring and vice-versa. Moreover, it adds a Spring profile and stereotypes for UML modeling. The Rational Software Architect edition can be installed with Rational Software Architect versions 7.5.2, 7.5.3, and 7.5.4.

Creating the tables

Due to the use of Hibernate as the JPA provider and the Hibernate Schema Updater, the step to create the physical database tables is not mandatory. The Hibernate Schema Updater will create the tables needed for the application if they do not already exist. This is a great feature for rapid application development, but in many enterprise environments the application will not have the permission to change the underlying table structure. Therefore the article demonstrates how to receive a PDM from an application model.

For more background information about when and why to use a logical data model and which development approach to choose, see the developerWorks article Integrating Rational Software Architect with Rational Data Architect.

The following example gives you deeper insight into the MDSD capabilities of this toolset.

Prerequisites

To enable LDM transformations in Rational Software Architect you need to apply the LogicalDataModel profile to the model. This feature has to be installed.

  1. Start the IBM® Installation Manager.
  2. Select Modify and the product to be modified (IBM® Rational® Software Delivery Platform).
  3. Click Next.
  4. Select InfoSphere Data Architect Integration under the topic Lifecycle and architecture tool integration, as shown in Figure 2.
Figure 2. Enable Data Architect Integration
Integration of InfoSphere Data Architect
  • To follow the examples given in this article, you need some familiarity with Spring, Spring webflow, and Rational Software Architect.
  • For a better understanding, watch the video "UML to Skyway" (10 min).
  • For installing the Skyway Builder request for a test drive and follow the installation instructions.
  • Links are provided in the Resources section.
  • The Rational Software Architect project is available in the Downloads section.

Model the application domain

You are ready to begin. You start by creating a new UML Project in the Modeling perspective of Rational Software Architect, as shown in the Skyway Video. The project is named ApplicationManagement-UML.

For LDM transformations, initially double-click your model and add the profile LogicalDataModel on the Details tab (Figure 3).

In the following section, you apply stereotypes for the Skyway DSL transformation and for the LDM transformation. If you do not intend to use a LDM, or there is already a data model in place, you don´t have to apply stereotypes from the LogicalDataModel profile.

Figure 3. Adding the Logical Data Model profile
Apply the Logical Data Model profile to your RSA model

So now that the preliminary work is done, you can proceed to model your first domain object (also known as an entity). You want to model a domain to manage (Web)-applications via JMX (Java Management Extensions). Therefore, you need to know the name of the applications to manage, the environment (production or test) that they are deployed to, their connection parameters, hosts, and so on.

  1. Create a package structure of your choice (for example, com.ibm.de.eas.application.management.domain)
    1. In the Project Explorer: right-click the model or package and select: Add UML > Package
    2. In each package there is a free form diagram automatically created, named Main, as shown in Figure 4.
      Figure 4. Create your package structure
      Apply your application structure by creating packages
  2. Double-click the Main free form diagram to model your domain classes. For this, select the DomainObject stereotype from the Skyway Spring palette, as shown in Figure 5, and click the free form diagram.
    Figure 5. Select a stereotype from the Skyway Spring palette
    Choose the Domain Object stereotype from the Skyway Spring palette
  3. Name your first Domain Object Application, as shown in Figure 6.
    Figure 6. The Domain Class Application
    Your first domain class named Application
  4. Move the mouse over the class frame, and a toolbar for adding attributes and operations is displayed.
    1. Click the first icon to add the primary key attribute.
    2. Type id:Integer into the editor, as shown in Figure 7, and press Enter.
      Figure 7. Add the primary key attribute
      Add the primary key to your domain object
  5. Now, add a String attribute.
    Type name:String into the editor, as shown in Figure 8, and press Enter.
    Figure 8: Add a String attribute
    Add a String attribute to your domain object
  6. Select the id - attribute and click the Stereotypes tab in the Properties view.
    1. Apply the stereotype Id from the Spring profile for the Skyway DSL transformation
    2. Apply the PrimaryKey stereotype from the LogicalDataModel profile, as shown in Figure 9. This is needed later for the transformation to a Logical Data Model.
      Figure 9. Apply the primary key stereotypes
      Apply all primary key stereotypes to your ID attribute
  7. After applying the PrimaryKey-stereotype a key-icon is visible to the left of the attribute, as shown in Figure 10.
    Figure 10. Primary key stereotype icon
    recognize the primary key stereotype icon
  8. Move the mouse over the class frame and arrows will appear to create references. Click an arrow and drag to a target to create a reference.
    1. In Figure 11, you create a many-to-many self-reference.
      Figure 11. Create a relationship
      Create the many-to-many self-reference relationship
    2. Select the reference and specify the multiplicity in the Properties view. Change the role names to manager and managed, as shown in Figure 12.
      Figure 12. Relationship roles and multiplicity
      change the role names of the relationship
  9. For the LDM transformation you have to tag this domain object as an entity stereotype. To do this, you select the class and apply a stereotype in the Properties view, as shown in Figure 13. Now the main work for a Domain Object is done.
    Figure 13. Apply the entity stereotype
    Apply the entity stereotype for the LDM transformation
  10. As described previously, you complete the domain model and add a few more attributes, entities, and references (as shown in Figure 14) to get something more robust than a Hello World example.
    Figure 14. The Application-Management domain model
    The completed Application-Management domain model

Although it is possible to model data access objects, the WebController, and Business Services (see Figure 5. Skyway Spring palette) in this example you stop here. you will generate these classes with the CRUD Scaffolding utility of the Skyway Builder. The Skyway integration with Rational Software Architect is bidirectional, and you are able to send the generated classes back into the application model.

The domain model is now ready to be transformed to Skyway DSL or to the LDM. But before you dive into the Skyway Builder, you continue with the Data Model Transformation to create the tables for an IBM® DB2® Version 9.7 database.


Transform to a data model

This section details the steps to transform a Rational Software Architect application model to a InfoSphere Data Architect LDM. With a few clicks you are able to get the Physical Data Model of a DB2 and the DDL scripts to create the tables.

UML to LDM Transformation

To transform a LDM from the UML class diagram, you need to create a Transformation Configuration in Rational Software Architect (remember the prerequisites shown in Figure 2. Enable Data Architect Integration).

  1. Select your UML Project and press Ctrl+N to open the Wizard Selection dialog box.
  2. Go to Transformations and double-click Transformation Configuration, as shown in Figure 15.
    Figure 15. Create the Transformation Configuration
    The wizard for creating a Transformation Configuration
  3. Go to Data Model Transformations and select UML to Logical Data Model
  4. Name the transformation (ApplicationManagement-Uml_TO_LogicalDataModel in Figure 16) and click Next.
    Figure 16. Create the UML to LDM Transformation Configuration
    Specify the UML to LDM Transformation Configuration and name it ApplicationManagement-UML
  5. Select the Application Management application model as the Selected source and the ApplicationManagement-UML project as the Selected target, as shown in Figure 17.
  6. Click Next.
    Figure 17. Specify source and target of the UML to LDM transformation
    Specify the elements that the transformation transforms, and specify a destination for the transformation output
  7. Limit the length for strings (255 in Figure 18).
  8. Click Finish.
    Figure 18. Set the default length for strings
    Specify the UML String length for this transformation
  9. Any time that you change your class model, you can transform it to an LDM by simply clicking Run on your Transformation Configuration, as shown in Figure 19.
    Figure 19. Start the LDM transformation
    Run the LDM transformation
  10. You can pick up your LDM file in the Navigator view, as shown in Figure 20. Simply right-click to copy it, then paste it to a Data Design project in InfoSphere Data Architect.
    Figure 20. The LDM file
    The tranformation output - domain.ldm

Now that the Logical Data Model is transformed, you need to create a Physical Data Model in InfoSphere Data Architect for the DB2 database.

Logical Data Model to Physical Data Model with InfoSphere Data Architect

InfoSphere Data Architect (formerly known as Rational Data Architect) is a visual data modeler. This example uses Version 7.5.2.

  1. Start InfoSphere Data Architect.
  2. Copy the domain.ldm file in Rational Software Architect.
  3. Paste your LDM file to a data design project in InfoSphere Data Architect, as shown in Figure 21.
    Figure 21. Import the Logical Data Model
    Paste the domain.ldm file into the Data Model folder
  4. Right-click the imported model and select Transform to Physical Data Model.
  5. Review the options. Change the Join Table separator to an underline ("_") and change the name of the database schema.
  6. Click Finish.
    Figure 22. Transform to a Physical Data Model
    Transform domain.ldm to a Physical Data Model
  7. Review the tables of your Physical Data Model. The keys ID and ID1 of the Join table APPLICATION_APPLICATION are not descriptive enough. According to the roles of the application Self-Reference (see Figure 12. Relationship Roles and Multiplicity) change the keys as shown in Figure 23 from
    • ID to MANAGER_ID
    • ID1 to MANAGED_ID
    Figure 23. Change key names of the join table
    Change the key names of the join table for the many-to-many reference
  8. Generate the DDL (right-click and select Generate DDL as shown in Figure 24), and then select Run SQL on the result script against a database connection.
    Figure 24. Generate DDL and Run SQL
    Generate the DDL

The tables are now created and you can continue to generate the Spring Web project. You will be able to populate the database tables with the CRUD application you will create in the next step. Alternatively you can use the DDL scripts from the Download section.


Transform to Skyway´s Spring DSL

you have created Spring and LDM stereotyped models and the tables for the entities. Now you use Skyway’s DSL transformation to transform the UML project into a Spring-enabled dynamic Java Web project that contains all of the appropriate Spring annotated classes and configuration files. You need a Skyway project for the transformation of the UML application model.

  1. In the Project wizard, double-click Skyway project. Name the project ApplicationManagement, as shown in Figure 25.
    Figure 25. Create a Skyway project
    Select the wizard New Skyway Project
  2. The Skyway project generates a skeleton Spring Dynamic Web project (ApplicationManagement-Web) and an enterprise archive, or EAR (ApplicationManagement-EAR) project. The Skyway Spring DSL model and the Web application are strictly separated, as shown in Figure 26. Generated and manual code are separated from each other physically. This follows best practices of MDSD.
    Figure 26. An empty Skyway Project
    Structure of an empty Skyway project
  3. Now you have to create the Transformation Configuration. that you start as described in UML to Logical Data Model Transformation, but you select the UML to Java Spring Transformation from Java Transformations, as shown in Figure 27. The source of the transformation is the UML model, while the target is the Dynamic Web project ApplicationManagement-Web.
    Figure 27.Create the UML to Spring Java Transformation
    Specify a configuration name and the transformation of type UML to Java Spring transformation
  4. Running the transformation populates the Spring DSL folder of the Skyway project with the domain objects from the UML model. At the same time, the Skyway project creates the Java classes into the generated folder of the Web project.
    Figure 28. The generated Spring DSL Model
    The transformation output: a Skyway model and the generated code

CRUD scaffolding

Skyway´s CRUD scaffolding enables you to generate Create, Read, Update and Delete functionality from an existing Skyway domain object.

  1. For each domain object, start the CRUD Generation: Right click and select Scaffolding > Generate CRUD, as shown in Figure 29.
    Figure 29. Scaffold your entities
    Generate a CRUD application by scaffolding your entities
  2. The engine generates packages for each layer (DAO, service, and Web). The Data Access Object got several finder methods to query the database. At this point the data source must be configured to resolve the errors (shown in Figure 30) concerning the connections to the database (must specify a connection).
    Figure 30. Scaffolding result
    The resulting Skyway model
  3. To configure the connection, double-click the DAO and select the Database Configuration tab. There you can select a datasource, which you can create in the Database Development perspective. (Have a look to the Import Types activity shown in Figure 31. Following a different development approach, you are able to create your domain objects by simply reverse engineering the database).
    Figure 31. Configure the DAO
    Bind the DAO to the database
  4. Now, let us have a look at the JPA (Java Persistence API) annotations of your generated Application Java domain class. You are not using the automatic table creation feature, because in this development approach the tables are already in place. You must be sure that the columns for the relations (Joincolumns) are mapped correctly. Therefore, you need to set the Joincolumn annotation. The JoinColumn for the jmxconnection relation is JMXCONNECTIONID, as shown in Listing 1.
    Listing 1. Generated Jmxconnection Reference of the Application class
    	@OneToOne(cascade = {
    			CascadeType.PERSIST,
    			CascadeType.MERGE,
    			CascadeType.REFRESH,
    			CascadeType.REMOVE }, fetch = FetchType.LAZY)
    	@XmlElement(name = "", namespace = "")
    	private Jmxconnection jmxconnection;

    To provide this information, do the following in the Skyway project, as shown in Figure 32:
    1. double-click the Application DomainObject in the Skyway Model to change the Persistence Mapping.
    2. Change the Map as field to ONE_TO_ONE.
    3. Change the Join Table to Application
    4. Add the Join Column named jmxconnectionid.
    Listing 2 shows the code after these changes.
    Figure 32. JoinColumn mapping
    Changing the relationship mappings
    Listing 2. The generated Jmxconnection reference after changing the persistence mapping
    	@OneToOne(cascade = {
    			CascadeType.PERSIST,
    			CascadeType.MERGE,
    			CascadeType.REFRESH,
    			CascadeType.REMOVE }, fetch = FetchType.LAZY)
    	@JoinColumns( { @JoinColumn(name = "jmxconnectionid") })
    	@XmlElement(name = "", namespace = "")
    	private Jmxconnection jmxconnection;

Again, doing this for all of the relations is repetitive and tedious work. So now you will learn how you can avoid this and see how easy it is to customize the generation of your entities.


Customize Skyway Templates

The code generation is based on JET templates. To adjust or change the code generation, you can create a Skyway Template project (see Figure 25. Create a Skyway project) and modify the templates. Here you change the DataType.jet template to modify the Join infomation to satisfy the enterprise conventions. Listing 3 shows the template before any changes.

  1. The JET Template for the DomainObject classes is ApplicationManagement-Templates\templates\com.skyway.integration.data.xml\templates\DataType.jet.
    Listing 3. Changing the JET template (before)
    line 01: <c:iterate select="$model/relationships" var="relationship">
    line 02: 
    line 03:   <persistence:relationshipType select="$relationship" />
    line 04: 
    line 05:   <persistence:joinColumns select="$relationship" />
    line 06: 
    line 07:   <persistence:joinTable select="$relationship" />
    line 08: 
    line 09:   <jaxb:xmlElement select="$relationship" dataType="$model" />
    line 10:   private <sw:declareClassVariable select="$relationship"><java:import>
                 <java:import><sw:javaType select="$relationship" package="true"/>
                 </java:import></sw:declareClassVariable>
    line 11: </c:iterate>
  2. You change the part where the iteration on the model references is taking place. For example, if you have a cardinality of ONE you write the JoinColumn Annotation, take the relationship-name and append id (see code line 10 in Listing 4, which shows the template after you modify it).
    Listing 4. Changing the JET template (after)
    line 01: <c:iterate select="$model/relationships" var="relationship">
    line 02:   <sw:relationshipCardinality select="$relationship" var="cardinality" />
    line 03: 
    line 04:   <persistence:relationshipType select="$relationship" />
    line 05: 
    line 06:   <c:if test="$cardinality = 'MANY'" >
    line 07:     @JoinTable(name = "<sw:javaType select="$model" package="false"/>_
                 <sw:javaType select="$relationship/targetDataType"/>")
    line 08:   </c:if>
    line 09:   <c:if test="$cardinality = 'ONE'" >
    line 10:     @JoinColumns( { @JoinColumn(name = "
                   <sw:getVariableName select="$relationship" capitalize="false"/>id") })
    line 11:   </c:if>
    line 12:   
    line 13:   <jaxb:xmlElement select="$relationship" dataType="$model" />
    line 14:   private <sw:declareClassVariable select="$relationship"><java:import>
                 <java:import><sw:javaType select="$relationship" package="true"/>
                 </java:import></sw:declareClassVariable>
    line 15: </c:iterate>
  3. For the given example you get a @JoinColumns( { @JoinColumn(name = "jmxconnectionid") }) after generation, as shown in Listing 5.
    Listing 5. Generated Jmxconnection reference after changing the JET template
    @OneToOne(cascade = {
      CascadeType.PERSIST,
      CascadeType.MERGE,
      CascadeType.REFRESH }, fetch = FetchType.LAZY)
    @JoinColumns( { @JoinColumn(name = "jmxconnectionid") })
    @XmlElement(name = "", namespace = "")
    private Jmxconnection jmxconnection;
  4. To enforce a new generation, delete the affected classes (in this example, delete the package ApplicationManagement-Web\generated\com\ibm\de\eas\application\management\domain") and clean all projects (Select Project > clean) Curious if this works ? Then try it to Deploy and Test the CRUD Application

Deploy and Test the CRUD application

Deploying to Apache Tomcat

If you are deploying to Apache Tomcat from IBM Rational Software Architect for IBM® WebSphere®, you will need to manage the build path manually. See the Resources section for more information on build path management in Eclipse.

Test the application

Start the server and type http://localhost:8080/ApplicationManagement-Web/indexApplication.html into your browser. You will see a table of all registered applications. Click the link in the Id column (as shown in Figure 33) and you will see the details of the selected application.

Figure 33. The Application Overview Page
The Application Detail page

Integrate Spring heritage code

You now know how to create a fully functional Spring MVC Web application from a UML Model with a few clicks. You enjoy great flexibility when adjusting the Code-Generation templates (for example, to fulfill the conventions that you might face in an enterprise environment). Next you learn how easy it is to incorporate your existing Java code to the application. Skyway provided two hooks for this.

The hooks

  1. Hook 1: The resources folder of the Web application contains Spring configurations for this purpose. Just add your code as a Spring bean to these files that are not marked as generated, as shown in Figure 34.
    Figure 34. Add handcoded Spring beans
    Add handcoded Spring Beans to th eSpring configuration files
  2. Hook 2: Double-click the Spring Dsl folder of the Skyway project, go to the Spring configuration tab and register your Spring configuration file to the Web or Service layer, as shown in Figure 35.
    Figure 35. Register your own Spring configuration file to the Skyway model
    Register Spring configuration files to the Skyway model

Adding the existing code

For the JMX example, you use the second hook and register the ApplicationManagement-jmx-context.xml file shown in Listing 6. This file contains some Spring magic. The hand coded bean com.ibm.de.eas.application.management.jmx.Administrator is exposed as a JMX MBean, and is therefore accessible for Administration consoles like JConsole or of course the Application Management Tool. You put our handmade code (see following listings 6 to 10) to the src folder of the Web project (see Figure 26. An empty Skyway project).

Listing 6. File ApplicationManagement-jmx-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:tx="http://www.springframework.org/schema/tx" 
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:jee="http://www.springframework.org/schema/jee" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
      http://www.springframework.org/schema/context 
      http://www.springframework.org/schema/context/spring-context-2.5.xsd
      http://www.springframework.org/schema/jee
      http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
                   	
      <!-- my administration mbean -->
      <bean id="admin" class="com.ibm.de.eas.application.management.jmx.Administrator"/>
	
      <bean id="jmxAttributeSource"
        class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

       <!-- will create management interface using annotation metadata -->
       <bean id="assembler"
        class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <property name="attributeSource" ref="jmxAttributeSource"/>
       </bean>

       <!-- will pick up the ObjectName from the annotation -->
       <bean id="namingStrategy"
         class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
         <property name="attributeSource" ref="jmxAttributeSource"/>
       </bean>
		
       <bean id="AdministratorExporter" 
          class="org.springframework.jmx.export.MBeanExporter">
        <property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
	<property name="exposeManagedResourceClassLoader" value="true"/>
	<property name="beans">
	  <map>
            <entry key="ApplicationManagement-Web:name=Administrator" value-ref="admin"/>
	  </map>
	</property>
	<property name="assembler" ref="assembler"/>
        <property name="namingStrategy" ref="namingStrategy"/>
        <property name="autodetect" value="true"/>
       </bean>	
</beans>

The Administrator class demonstrates two show cases:

  • The value attribute is readable and writeable from a remote application (like JConsole) through the exposed methods readValue and writeValue.
  • The bean is a Singleton which reads the Application-Domain-Objects from the database using the ApplicationService that is generated by the Skyway CRUD Scaffolding utility. For each Application instance, the getApplicationProxies() method creates a JMX proxy. With the help of the proxy your application is able to access each registered Remote-Application and read its operating system data. Of course you can register a new application with the Skyway generated CRUD application.

The Administrator.java file is shown in Listing 7.

Listing 7. File ApplicationManagement-Web/src/com/ibm/de/eas/application/management/jmx/Administrator.java
package com.ibm.de.eas.application.management.jmx;

import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;

import com.ibm.de.eas.application.management.domain.Application;
import com.ibm.de.eas.application.management.service.ApplicationService;

/**
 * 
 * Example of a Bean which is exposed by Spring as a MBean 
 * The attribute value can be changed by a JMX Administration Console
 *
 */
@ManagedResource(objectName = "ApplicationManagement-Web:name=Administrator", 
		description = "JMX Administration")
public class Administrator implements BeanFactoryAware {

	private static Logger logger = Logger.getLogger(Administrator.class);

	private transient Set<ApplicationProxy> applicationProxies = null;

	/**
	 * This Service is generated with Skyways CRUD Scaffolding
	 * The Bean is automatically wired by Spring´s Dependency Injection
	 */
	@Autowired
	private transient ApplicationService applicationService;

	private String value = "default";

	/**
	 * reads Application data from the database and returns JMX Proxies for each
	 * registered application
	 * 
	 * @return Set<ApplicationProxy>
	 * @throws Exception
	 */
	public Set<ApplicationProxy> getApplicationProxies() throws Exception {
		if (applicationProxies == null)
			applicationProxies = new HashSet<ApplicationProxy>(1);

		if (applicationProxies.size() == 0) {
			Set<Application> applications;
			// read the registered Applications from the database ...
			applications = applicationService.loadApplications();

			for (Application application : applications) {
                           // ... and create JMX Proxies for each application
                           applicationProxies.add(new ApplicationProxyImpl(application));
			}
		}
		return applicationProxies;
	}

	public String getValue() {
		return value;
	}

	/**
	 * Test Method, this is accessible via JMX 
	 * @return String value
	 */
	@ManagedOperation(description = "readValue")
	public String readValue() {
		logger.debug("get value " + value);
		return value;
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(
	 *      org.springframework.beans.factory.BeanFactory)
	 */
	@Override
	public void setBeanFactory(BeanFactory bf) throws BeansException {
		// make the Spring Context accessible for each application in this jvm
		// not usable for production environments, 
		//  concurrent applications would overwrite their reference
		AppContext.set(bf);
	}

	
	public void setValue(String value) {
		this.value = value;
	}

	/**
	 * This Method is accessible from a JMX Administration Console
	 * 
	 * @param value
	 * @return the value changed
	 */
	@ManagedOperation(description = "writeValue")
	@ManagedOperationParameters( { @ManagedOperationParameter(name = "value", 
			description = "value") })
	public String writeValue(String value) {
		this.value = value;
		logger.debug("value " + value + " set");
		return "value " + value + " set";
	}
}

Listing 8 shows the interface to retrieve data from a remote application. You use these methods to visualize the data in a table served by a JSP page.

Listing 8.The ApplicationProxy interface
package com.ibm.de.eas.application.management.jmx;

import java.lang.management.OperatingSystemMXBean;

import com.ibm.de.eas.application.management.domain.Application;

/**
 * JMX Proxy Interface Example.
 * 
 * access the OperatingSystemMXBean of a remote application to read
 * some data like heap size, etc.
 * 
 */
public interface ApplicationProxy {
	public abstract OperatingSystemMXBean getOperatingSystem();
	public abstract String getUrl();
	public abstract String getServerUrl();
	public Application getApplication();
}

The code in Listing 9 is a simplified solution for demonstration purposes. The sources are not ready for production. For further explanation, see the Spring Framework reference. An in-detail explanation is not in the scope of this article.

Listing 9. The implementation of ApplicationProxy
package com.ibm.de.eas.application.management.jmx;

import java.io.Serializable;
import java.lang.management.OperatingSystemMXBean;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.management.MBeanServerConnection;

import org.apache.log4j.Logger;
import org.springframework.jmx.support.MBeanServerConnectionFactoryBean;

import com.ibm.de.eas.application.management.domain.Application;

/**
 * Only for demonstration purposes !
 * 
 * This class creates a JMX Proxy of a given Application Domain Object
 * Internally it creates a MBeanServerConnection and registers itself and
 * the Remote OperatingSystemMXBean of the Application
 */
final public class ApplicationProxyImpl implements ApplicationProxy {

 /**
  * wraps the jmx url for a given application
  */
 final class JmxConnection implements Serializable {
  
  private static final long serialVersionUID = 6234025617250549332L;
  private Application application = null;
  private Integer namingPort;
  private Integer port;
  private String serverUrl;

  public JmxConnection(Application application) {
   super();
   this.port = application.getJmxconnection().getPort();
   this.namingPort = application.getJmxconnection().getNamingPort();
   this.application = application;
   createUrl();
  }

  private void createUrl() {
   String hostName = application.getContainer().getHost().getName();
   String appName = application.getWebappName();
   if (namingPort == null || namingPort == 0) {
    serverUrl = "service:jmx:rmi:///jndi/rmi://" + hostName + ":"
      + port + "/jmxrmi";
   } else {
    serverUrl = "service:jmx:rmi://" + hostName + ":" + namingPort
      + "/jndi/rmi://" + hostName + ":" + port + "/"
      + appName;
   }
  }

  public Integer getPort() {
   return port;
  }

  public String getServerUrl() {
   return serverUrl;
  }

  @Override
  public String toString() {
   return getServerUrl();
  }
 }

 private static Logger logger = Logger.getLogger(ApplicationProxyImpl.class);
 private static final String OPS = "ops";
 private Application application = null;
 /**
  * Spring ID MBeanServer
  */
 private String idMBeanServer = null;
 private JmxConnection jmxConnection = null;
 private Map<String, Object> proxies = new HashMap<String, Object>();
 private String url = null;

 public ApplicationProxyImpl(Application application) {
  super();
  this.application = application;
  String host = application.getContainer().getHost().getName();
  String app = application.getName();
  idMBeanServer = host + app;
  url = "http://" + host + ":" + application.getPort() + "/"
    + application.getWebappName();
  jmxConnection = new JmxConnection(application);
 }

 private void createMBeanServerConnection() {
  try {
   if (jmxConnection != null) {
    String surl = jmxConnection.getServerUrl();
    Properties p = new Properties();
    p.put("serviceUrl", surl);
    MBeanServerConnection conn = (MBeanServerConnection) AppContext
      .registerBean(idMBeanServer,
        MBeanServerConnectionFactoryBean.class, p);
    logger.info("JMX MBean Server registered to " + idMBeanServer
      + " with " + surl);
    registerProxies(conn);
   }
  } catch (Exception e) {
   logger.error(e.getMessage(), e);
  }
 }

 public Application getApplication() {
  return application;
 }

 /*
  * (non-Javadoc)
  * 
  * @see com.ibm.de.eas.application.management.jmx.ApplicationProxy#
  * getOperatingSystemMBean()
  */
 public OperatingSystemMXBean getOperatingSystem() {
  // create MBean and
  // register ops to spring container
  return (OperatingSystemMXBean) getProxies().get(OPS);
 }

 private Map<String, Object> getProxies() {
  if (proxies.size() == 0) {
   try {
    createMBeanServerConnection();

   } catch (Exception e) {
    logger.warn("Problem with JMX Connection: " + e.getMessage());
    proxies.clear();
   }
  }
  return proxies;
 }

 public String getServerUrl() {
  return jmxConnection.toString();
 }

 public String getUrl() {
  return url;
 }

 /**
  * register MBean Proxy to the Spring DI-Container
  */
 private void registerProxies(MBeanServerConnection mbeanServerConnection) {
  String id = OPS;
  Object proxy = AppContext.registerMBean(id + idMBeanServer,
    "java.lang:type=OperatingSystem", OperatingSystemMXBean.class,
    mbeanServerConnection);
  proxies.put(id, proxy);
 }
}

Listing 10 shows the Spring registration helper class.

Listing 10. The Spring registration helper class
package com.ibm.de.eas.application.management.jmx;

import java.util.Enumeration;
import java.util.Properties;

import javax.management.MBeanServerConnection;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.jmx.access.MBeanProxyFactoryBean;

public class AppContext {
   static BeanFactory beanFactory = null;
   private static Logger logger = Logger.getLogger(AppContext.class);

   public static BeanFactory get() {
      return beanFactory;
   }

   public static void set(BeanFactory bf) {
      beanFactory = bf;
   }

   /**
    * This method uses Spring to register and create MBeans dynamically
    * 
    * @param id - Spring Context id
    * @param objectname - the name of the MBean
    * @param proxyInterface - the interface of the MBean
    * @param mbeanServerConnection - the connection to access the MBean
    * @return - the instance of the MBean
    */
   @SuppressWarnings("unchecked")
   public static Object registerMBean(String id, String objectname,
         Class proxyInterface, MBeanServerConnection mbeanServerConnection) {
      try {
         
         Properties props = new Properties();
         props.put("objectName", objectname);
         props.put("proxyInterface", proxyInterface);
         props.put("server", mbeanServerConnection);
         
         
         registerBean(id, MBeanProxyFactoryBean.class, props);
         
         return AppContext.get().getBean(id);

      } catch (Throwable t) {
         logger.warn("Error creating the MBean Proxy " + objectname
               + ": " + t.getMessage(), t);
      }
      return null;
   }

   /**
    * 
    * Registers a bean to the Spring Context at runtime
    * 
    * @param id - Spring Context id
    * @param clazz - Class to register to Spring
    * @param props - Properties to set to the Instance of clazz
    * @return
    */
   @SuppressWarnings("unchecked")
   public static Object registerBean(String id, Class clazz, Properties props) {

      BeanDefinitionBuilder builder = BeanDefinitionBuilder
            .rootBeanDefinition(clazz);

      Enumeration<Object> keys = props.keys();
      while (keys.hasMoreElements()) {
         Object k = (Object) keys.nextElement();
         builder.addPropertyValue(k.toString(), props.get(k));
      }

      ((BeanDefinitionRegistry) AppContext.get()).registerBeanDefinition(id,
            builder.getBeanDefinition());
      
      return get().getBean(id);
   }

}

Extend the application with a webflow

Now that your JMX infrastructure is ready to run, you finally have to create a view to see the data in your managed applications. You will use Skyway´s visual editor for Spring Web Flow (SWF) to create a simple Master Detail user flow. SWF is used to model user interactions occurring in several requests.

Create the flow

  1. First, create a model package in the Skyway project, then create a webflow using the Web Flow wizard (right-click and select New > Web Flow), as shown in Figure 36.
  2. Name the webflow admin.
  3. The Web Flow wizard will ask for a location for where to file the SWF model (admin.xml, the Spring configuration file for webflows). Save it to ApplicationManagement-Web\WebContent\WEB-INF\flows.
    Figure 36. Create a Spring Webflow
    Create a Spring Webflow by using Skyways Visual Builder toolset
  4. Next, build your webflow by simply dragging the States and Transitions to the canvas.
    1. Define the backing bean as a webflow variable.
      • Press the plus ("+") sign on the Flow configuration element
      • Insert the variable name (adminView)
      • Insert the name of your View class (com.ibm.de.eas.application.management.flow.admin.Admin)
      Now the View bean com.ibm.de.eas.application.management.flow.admin.Admin is accessible from a JSP page, or from the webflow with the adminView expression.
    2. Prepare the first part of the webflow to test.
      • Drag a View state and name it admin.
      • Link the Flow configuration element with the View state using a transition.
      • Name the transition admin.
      The first part of the webflow is ready to test now. If an admin event occurs (URL or button click), the admin webflow will go into the view state admin. Because you did not specify any page for this view state, by convention admin.jsp will be served to the user.
    3. Do the same with another view state.
      • Drop another View state and name it viewOperatingSystemDetail.
      • Link the Flow configuration element with the View State viewOperatingSystemDetail using a transition.
      • Name the transition showOperatingSystemDetail.
      If an event showOperatingSystemDetail occurs (URL or button click), the admin webflow will go into the View state viewOperatingSystemDetail. Because you did not specify any page for this View state, by convention viewOperatingSystemDetail.jsp will be served to the user.
    4. To be able to return to the admin View state:
      • Define an End state and give it a name (for example, goBack).
      • Link the View state with the End state using a transition.
      • Name the Transition (for example, goBack).
      The results are shown in Figure 37
    Figure 37. Create a Spring webflow
    Build your Spring Webflow by dragging the States and Transitions

    You can now see the code resulting from your visual modeling. Skyway generated the admin.xml shown in Listing 11, a JSP for the admin View state and one JSP for the second View state.

    Listing 11. Spring Webflow model admin.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns="http://www.springframework.org/schema/webflow" 
        xsi:schemaLocation="http://www.springframework.org/schema/webflow 
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
      
      <var class="com.ibm.de.eas.application.management.flow.admin.Admin" 
                name="adminView"/>
      <view-state id="admin"/>
      <view-state id="viewOperatingSystemDetail">
        <transition on="goBack" to="goBack"/>
      </view-state>
      <end-state id="goBack"/>
      <global-transitions>
        <transition on="admin" to="admin"/>
        <transition on="showOperatingSystemDetail" to="viewOperatingSystemDetail"/>
      </global-transitions>
    </flow>
  5. Now you handcraft the view class that is defined by the flow and referenced by the JSP page. By accessing the getRemote() method the JSP page is able to present the operational data of each persisted application. The reference to your administration MBean is populated by Spring´s Dependency Injection (@Autowired annotation), as shown in Listing 12.
    Listing 12. View Bean com.ibm.de.eas.application.management.flow.admin.Admin
    package com.ibm.de.eas.application.management.flow.admin;
    
    import java.io.Serializable;
    import java.util.Set;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    
    import com.ibm.de.eas.application.management.jmx.Administrator;
    import com.ibm.de.eas.application.management.jmx.ApplicationProxy;
    
    public class Admin implements Serializable {
       
       /**
        * 
        */
       private static final long serialVersionUID = -486243002544361404L;
       
       @Autowired
       @Qualifier("admin")
       private transient Administrator administrator;
       
       public String getValue(){
          return administrator.getValue();
       }
       
       public void setValue(String value){
          administrator.setValue(value);
       }
          
       public Set<ApplicationProxy> getRemote() throws Exception {
          return administrator.getApplicationProxies();
       }   
    }
  6. As defined before, you can access the view class in the JSP page with adminView. To access attributes (or methods) use expressions like ${adminView.value}. In the JSP specification Version 2.0 and later, the expression language is incorporated. This expression causes a call of the getValue() method. To iterate over the result of the getRemote() method, use a iteration variable (for example, current), which is of type ApplicationProxy. To access attributes of relations of ApplicationProxy, use expressions like ${current.application.name}. The button for entering the transition to the detail view is as follows: <input type="submit" name="_eventId_showOperatingSystemDetail" value="Details"/>. showOperatingSystemDetail is the transition that you modeled to enter the view state viewOperatingSystemDetail. Listing 13 illustrates these points.
    Listing 13. Enhance the admin.jsp
    <%@ page language="java" isELIgnored="false" 
          contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
    <jsp:directive.include file="/WEB-INF/sitemesh-decorators/include.jsp"/>
    <html>
    <head>
     <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
     <title>Administration</title>
     <skyway:javascript src="prototype.js"></skyway:javascript>
     <skyway:javascript src="skyway.js"></skyway:javascript>
     <skyway:javascript src="skyway-internal.js"></skyway:javascript>
    </head>
    <body>
    <skyway:form action="${flowExecutionUrl}" commandName="">
    <skyway:label value="${adminView.value}"></skyway:label>
    <br><br>
        <table id="applicationTable">
            <thead>
            <tr>
                <th>ID</th>
                <th>Application</th>
                <th>Console Url</th>
                <th>Jmx Url</th>            
            </tr>
            </thead>
            <tbody>
        <c:forEach items="${adminView.remote}" var="current" varStatus="i">
            <c:choose>
                <c:when test="${(i.count) % 2 == 0}">
                    <c:set var="rowclass" value="tableRow1"/>
                </c:when>
                <c:otherwise>
                    <c:set var="rowclass" value="tableRow2"/>
                </c:otherwise>
            </c:choose>            
            <tr class="${rowclass}">                        
                <td>${current.application.id}</td>
                <td>${current.application.name}</td>
                <td>${current.url}</td>            
                <td>${current.serverUrl}</td>            
            </tr>
        </c:forEach>
            </tbody>
        </table>
    <input type="submit" name="_eventId_showOperatingSystemDetail" value="Details" />
    </skyway:form>
    </body>
    </html>
  7. To show the details, as shown in Listing 14, you use the view class as described before, but access the OperatingSystem and MBean methods: ${current.operatingSystem.systemLoadAverage}.
    Listing 14. enhance viewOperatingSystemDetail.jsp
    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 
        pageEncoding="ISO-8859-1"%>
    <jsp:directive.include file="/WEB-INF/sitemesh-decorators/include.jsp"/>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
        "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Operating System Details</title>
    </head>
    <body>
    <skyway:form action="${flowExecutionUrl}" commandName="">
    <skyway:label value="Operating System Details"></skyway:label><br><br>
        <table id="opsTable">
            <thead>
            <tr>            
                <th>Application</th>
                <th>Name</th>
                <th>Version</th>            
                <th>AvailProc</th>
                <th>Load</th>
                <th>Arch</th>
            </tr>
            </thead>
            <tbody>
        <c:forEach items="${adminView.remote}" var="current" varStatus="i">
            <c:choose>
                <c:when test="${(i.count) % 2 == 0}">
                    <c:set var="rowclass" value="tableRow1"/>
                </c:when>
                <c:otherwise>
                    <c:set var="rowclass" value="tableRow2"/>
                </c:otherwise>
            </c:choose>            
            <tr class="${rowclass}">
                <td>${current.application.id}:${current.application.name}</td>
                <td>${current.operatingSystem.name}</td>
                <td>${current.operatingSystem.version}</td>
                <td>${current.operatingSystem.availableProcessors}</td>
                <td>${current.operatingSystem.systemLoadAverage}</td>
                <td>${current.operatingSystem.arch}</td>
            </tr>
        </c:forEach>
            </tbody>
        </table>
        <input type="submit" name="_eventId_goBack" value="Back" />
    </skyway:form>
    </body>
    </html>

Test the flow

Open the launch configuration of your server and configure the JMX Mbean server for your hand-coded part. Add the code shown in Listing 15:

Listing 15. JVM parameter to enable JMX
-Dcom.sun.management.jmxremote.port=9001
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Start the server and insert http://localhost:8080/ApplicationManagement-Web/admin. The admin in the URL is the transition that you modeled before. With this URL, the admin SWF starts and serves the admin.jsp, as shown in Figure 38.

Figure 38. The Admin view
The flow view state admin

When you click the Details button, the viewOperatingSystemDetail view state is entered and the corresponding JSP is presented to the user, as shown in Figure 39.

Figure 39. The Operating Systems view
The flow view state showOperatingSystemDetail

What you have learned

In this article, you learned how to generate a fully functional Spring application based on model transformations. You transformed an application model to a logical data model for finally creating the database tables. Then you transformed the application model to the Spring DSL to scaffold the Spring Web application in minutes. You viewed the changing of the JPA Entity table mappings with Skyway´s visual editor for data types. You learned how to achieve the same thing by changing the JET code generation template. Then, you learned about the separation of generated and manually crafted source code, and how to add handcrafted code to the project. Finally you used the Visual Builder for Spring webflow to model a user interaction that references the heritage code.

The Rational Software Architect, InfoSphere Data Architect, and Skyway toolset is a significant help to simplify and to accelerate the delivery process of software. It focuses your concentration on the model and minimizes your attention to implementation details. The time savings, especially for huge applications, is tremendous. If you are a beginning user of Java and Spring gets a solid, well-designed Spring Web application that is easy to extend. Design mistakes are prevented early, and you accelerate your learning about developing Spring applications. Due to the Eclipse EMF base, you can even extend the Spring DSL model to catch your sophisticated needs.

Now then, when will you be walking the Skyway?


Acknowledgements

Thanks to Dr. Jürgen Herrmann and Martin Braun for reviewing and commenting on this article. Thanks to Dr. Klaus Schlöter and the SDC team for their valuable feedback at the Skyway live session.


Downloads

DescriptionNameSize
RSA Model1ApplicationManagement-UML.zip23KB
JET Template2ApplicationManagement-Templates.zip4KB
Java-, JSP-, Spring configuration- sources3ApplicationManagement-Web.zip12KB
DDL Scripts4ddl.zip2KB

Notes

  1. The complete Rational Software Architect modeling project as a Project Interchange File
  2. Create a Skyway Template project and paste the content of this zip into the project
  3. The handcrafted code: paste the content of this zip into the generated Web project
  4. The generated DDL and the script to populate the tables

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, Java technology
ArticleID=483084
ArticleTitle=Walk the skyway: Generating Spring applications with Rational and the Skyway Builder
publish-date=04202010