Search This Blog

Loading...

Monday, October 31, 2011

Flex4: Automatic Data grid generation using reflection in Action Script 3

When building web applications in flex 4 using blazeds services, the most straightforward option to present raw datas is to use a Datagrid (or AdvancedDatagrid). Usually the client side developer must implement some data visualization and manipulation component, which heavily relies on datagrid usage. I found myself doing every time the same things: cut and paste, change String values, rename some variables. Such a process is time consuming and do not really add some values to the programmer work, it is just mechanic action quitting time to real problem solving activities.
Using the reflection capabilities of Actionscript 3, you can forget about repetitive task such the one above still retaining a certain degree of collaboration with your designer. The following tutorial shows how you can write your own little framework for data-grids automatic generation and visualization. The example can be further modified to implement also more complex visualization requirement using the same reflection capability.
Reflection in as3 does not provides the same functionalities of the Reflection API in Java, still provides some tools useful to automate some tasks such that automatic Datagrid generation and visualization. In this example, we have a simple web application using remote services provided by a blazeds server. We have this simple class replying an imaginary db table named "Employee" (I omit remoting specific metadata definition for now):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package
{
  public class Employee
  {
    public function Employee()
    {
    }
    [Column(name="Id")]
    public var id:int;
    [Column(name="Name")]
    public var name:String;
    [Column(name="Surname")]
    public var surname:String;
    [Column(name="Department")]
    public var department:String;
  }
}
We can note the usage of a custom metadata tag named Column. We will use such a information to initialize Datagrid's headerText and to inform our routine that the specific property must be included in a column. We create now a custom component in flex 4, containing just a Datagrid and a PopUpMenuButton as follow:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" 
     xmlns:s="library://ns.adobe.com/flex/spark" 
     xmlns:mx="library://ns.adobe.com/flex/mx" width="632" height="300"
     creationComplete="init()">
  <fx:Declarations>
    <!-- Place non-visual elements (e.g., services, value objects) here -->
  </fx:Declarations>
  <mx:DataGrid x="124" y="29" width="400" height="95" id="grid">
  </mx:DataGrid>
  <mx:PopUpMenuButton x="124" y="0" label="Hide/Show columns"/>
 
  <fx:Script>
    <![CDATA[
      public var cls:Object;
 
      private function init():void {
        //More to come later
      }
    ]]>
  </fx:Script>
</s:Group>
Note that we define a public variable of a generic type Object. Such a variable must be istanziated by the containing component to a object of the type Employee (if we want to display Employee data, but the point is that we can pass any object in here).
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" 
         minHeight="600"  xmlns:ns1="*">
        <fx:Declarations>
    <!-- Place non-visual elements (e.g., services, value objects) here -->
    <ns1:Employee id="badulo"/>
  </fx:Declarations>
  <ns1:customGrid horizontalCenter="0" verticalCenter="0" cls="{badulo}">
  </ns1:customGrid>
</s:Application>
At this point, we can make use of the Reflection capability of as3, using the describeType function of the flash.utils package. I'm not going to give a deep description of this function as internet is full of references, I just say that it returns an XML object containg a full bunch of informations about the type Class of its parameter. We focus on the XMLList containg the variables description and with a bit of xml processing we are able to automatically create columns defining headerTexts and dataFields without a line of mxml code. As always, we want to generate a drop-in component reusable in any project which can free us from annoying tasks. So, let's define an as3 Class responsible of doing all the background processing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package
{
  import flash.utils.describeType;
 
  import mx.controls.dataGridClasses.DataGridColumn;
 
  public class ColumnsGenerator
  {
    private var cls:Object;
 
    public function ColumnsGenerator(cls:Object)
    {
      this.cls = cls;
    }
 
    public function generateColumns():Array {
      var desc:XML = describeType(cls);
      var vars:XMLList = desc.variable;
      var cols:Array = new Array();
      for each (var v:XML in vars) {
        var mdValue:String;
        if ((mdValue = getHeaderText(v.metadata)) != null) {
          var col:DataGridColumn = new DataGridColumn();
          col.headerText = mdValue;
          col.dataField = v.@name;
          cols.push(col);
        }        
      }
      return cols;
    }
 
    private function getHeaderText(md:XMLList):String {
      for each (var v:XML in md) {
        if (v.@name == "Column") {
          return v.arg[0].@value;
        }
      }
      return null;
    }
  }
}
The generateColumns function process the xml and looks for our custom metadata informations. If they are present, the DatagridColumn is initialized and added to the array. Note that we are almost done, it remains to call the function in our custom component and update the columns of the datagrid:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" 
     xmlns:s="library://ns.adobe.com/flex/spark" 
     xmlns:mx="library://ns.adobe.com/flex/mx" width="632" height="504"
     creationComplete="init()">
  <fx:Declarations>
    <!-- Place non-visual elements (e.g., services, value objects) here -->
  </fx:Declarations>
  <mx:DataGrid x="124" y="29" width="400" height="95" id="grid">
  </mx:DataGrid>
  <mx:PopUpMenuButton x="124" y="0" label="Hide/Show columns"/>
  <s:TextArea x="0" y="123" width="632" height="177" id="ta"/>
  <s:TextArea x="0" y="308" width="632" height="196" id="ta1"/>
  <fx:Script>
    <![CDATA[
      public var cls:Object;
 
      private function init():void {
        var cg:ColumnsGenerator = new ColumnsGenerator(cls);
        grid.columns = cg.generateColumns();
      }
    ]]>
  </fx:Script>
</s:Group>
At this point, you can use the preceding tutorial to add automatic columns hide/show functionality with a little modification. The result is a totally automated datagrid generation and visualization logic embedded into a custom component, which can be reused in any part of your project just passing to it a specific Class instance. You can also add more custom metadatas to your as3 Classes to address some issues as columns order and renderer definition using Reflection. As a final comment, note that you must add the directive
-keep-as3-metadata “Column”
to your compiler to retain custom metadata information at runtime.

Sunday, October 30, 2011

JDeveloper: Setting up JRebel with JDeveloper 11g

The following provides a quick walkthrough on how to enable JRebel for a JEE web project in the Oracle’s JDeveloper IDE. JDeveloper version 11g was used for creating this tutorial, but other versions are expected to work similarly.


Now you just need a web application and to enable JRebel for it. To do it, two steps are necessary:
  1. enabling JRebel in application server’s launch configurations for the current web-app
  2. adding the JRebel’s project-specific configuration file (rebel.xml) into your project’s classpath


0. Creating a web project
This is the “step 0″ as probably you’ll want to enable JRebel for a web project you already have. Anyway, for this tutorial we created a demo application with the Create Web Project wizard (right click in Application Navigator,New .. -> General -> Projects -> Web Project).
To demonstrate JRebel working, a servlet and a JSP page were added to the project. While adding a JSP page was a matter of checking the “include JSP page” box during the web project creation, adding a servlet was just as easy by right-clicking on the project name (in Projects navigator) -> New.. ->Web Tier -> Servlets -> HTTP Servlet. We should now have a project structure something like the following:

1. Enabling JRebel in app-server’s launch configuration

First you need to tell the IDE to start the application server (WebLogic by default) with the JRebel’s javaagent. Go through the following steps:
  1. Right-click on your project in the Application Navigator, open Project Properties..
  2. Open the Run/Debug/Profile tab
  3. Open the edit dialog for the chosen run configuration (click Edit..)
  4. In the Launch Settings tab, edit the Java Options. Append all the desired configuration options for JRebel. The minimal install string is: -noverify -javaagent:C:\path\to\jrebel\jrebel.jar, where the javaagent path is adjusted according to your local JRebel installation path.


Verification: When you start your application on the application server (by right-clicking on your servlet-file or your JSP-file and hitting Run..) for the first time and observe the server log, you have to see a large JRebel banner near the beginning of the server log. Something like this:

...
starting weblogic with Java version:
Starting WLS with line:
/home/sander/apps/oracle-jdev/jdk160_18/bin/java -client   -Xms256m -Xmx512m ...

#############################################################

 JRebel 4.5.1
 (c) Copyright ZeroTurnaround OU, Estonia, Tartu.

 Over the last 1 days JRebel prevented
 at least 9 redeploys/restarts saving you about 0.4 hours.

 This product is licensed to Mohamed Mahmoud Taman.
 for non-commercial use only.

 The following plugins are disabled at the moment:
 * EclipseLink Plugin (set -Drebel.eclipselink_plugin=true to enable)
Reloads EntityManagerFactory when configuration changes
 * Grails Plugin (set -Drebel.grails_plugin=true to enable)
...
#############################################################

Server startup options can also be modified in the server’s Properties view accessed through the Application Server Navigator, but this doesn’t seem to work. The configuration dialog also includes a warning that these settings are used only when starting the server without any application selected… anyway, somehow the settings are not picked up and the server starts up without JRebel (you know it by seeing no JRebel banner in the beginning).

2. Creating the rebel.xml configuration file

You also need to create a project-specific JRebel configuration file (rebel.xml) to tell JRebel which locations to monitor for the updated versions of class files and other resources. The rebel.xml descriptor needs to end up in your project’s classpath (under WEB-INF/classes).
Probably the quickest is to right-click on Application Sources, pick New.. -> All Items -> File. For further information on rebel.xml configuration file refer to the appropriate documentation; anyway, here’s an example:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<application>
    <classpath>
        <dir name="/home/sander/dev/jdeveloper/DemoProject/classes" />
    </classpath>
    <web>
        <link target="">
            <dir name="/home/sander/dev/jdeveloper/DemoProject/public_html" />
        </link>
    </web>
</application>
The two things here are the path to the directory where your IDE compiles the classes and the path to the directory where your IDE constructs the so-called “web root” directory (i.e. a directory that contains mostly the same things as the war-file after extracting). It is not important that the web root directory also contains the WEB-INF/classes and WEB-INF/lib directories, as the classes are picked up from the path in the <classpath>and libraries are not expected to change.
The path to where JDeveloper is building the classes is defined by the Output Directory setting in the Project Properties -> Project Source Paths view; the default value for it is $PROJECT_ROOT/classes. The web root of the web project should also be located directly in the project’s root directory; by default it is$PROJECT_ROOT/public_html.


Run the application on the server to test whether JRebel has been set up properly. In our case, the easiest is to direct our browser to the URL where the auto-generated servlet is just outputting some HTML:

public class Servlet1 extends HttpServlet {
    // ..
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>Servlet1</title></head>");
        out.println("<body>");
        out.println("<p>The servlet has received a GET. This is the reply.</p>");
        out.println("</body></html>");
        out.close();
    }
}

Obviously, without JRebel’s code reloading, changing anything in this Java code wouldn’t be seen in the browser until we redeploy. Convince yourself that recompiling the edited servlet results in the output also changing in the browser after hitting F5 (if you did configure everything correctly).

Thanks JRebel development and blog for information.