Wednesday, November 10, 2010

Using Ant with NatJet

In this post, I will present a quite standard Ant build file for a NatJet project. Notice, that as NatJet is a standard Dynamic Web Project in Eclipse, this will also work for any web project having the same structure.

A NatJet project has by default the following structure :

  • src : source directory
  • build : a temporary directory where class files are generated
  • WebContent : a directory that stores files that will be deployed on the web server

I won’t present Ant : I will use it as an automate for integration.

Integration

For me, the main goal of integration is to insure consistency between source and binary. It is not just producing a binary for production : integration has to be the guardian of this consistency.

Second point, I’m using the Run As –> On server menu to test while in development. Ant will only be used in the integration process to deliver a war to a customer for test or production. This means that my configuration is not optimized to speed integration but to insure the consistency.

The way I will use Ant, is directly inspired by this quest. This is important because each solution has strengths and weaknesses : I will therefore be more interested in strength that insure consistency than speed.

Delivery for test or production brings generally some new constraints :

  • database connection is not the same as in development
  • sometimes there are other parameters or some specific constraints to the production environment

In my sample, I will have two specificities to handle :

  1. for database connection, I’m using a datasource which is defined in the context.xml file that is in META-INF repository. I need two different files : one for development one for production. There is no need to insure a consistency between the two files.
  2. as I’m using a datasource, the hibernate.cfg.xml file needs the DTD declaration. This declaration is a problem : either I use an URL but in this case the server needs a internet access or I put a file with a relative path, but in this case the path has to be changed for every server. Anyway, I can’t use the same declaration for development and production. The hibernate.cfg.xml file needs to be the same between both environments : there is only one line that needs to change : the DTD declaration. A hibernate file may evolve quite a lot. In this case, I do not want to have to maintain two files.

Functionalities of my ant build.xml

As I’ve stated it before, the default task of my build.xml will be the the war task that build the war file. The default configuration will correspond to the one of the production environment. There will be defined in the build.properties file.

If you are in a hurry and quite familiar with ant, you may prefer to skip this section and go directly to the two files :Summary : the two files

The sample build.xml ant file will also :

  1. exclude the java files for the jUnit test from the compile task
  2. replace a line in the hibernate.cfg.xml file depending on the configuration
  3. pick up the right context.xml file
  4. exclude xml files corresponding to the panel description

Finally, I have define two ant task to modify file according to the environment

Default task to build a war

The first tag is project tag. This tag defines :

  • project name : It’s just for me a way to attach the build.xml file to a project
  • basedir : It’s usually “.” meaning the root of the eclipse project
  • default : this is the default task, that is highlighted. In my case is will be war. Pepel using ant in the development process use build or compile task as a default.

In my case, it will be :

<project name="my-natjet-project" basedir="." default="war">

As I want to insure consistency of source and binary (the war file), I will have the following chain dependency of my ant tasks :

  • <target name="clean">
  • <target name="copy-non-java-files">
  • <target name="compile" depends="clean,copy-context-delivery,copy-non-java-files">
  • <target name="war" depends="compile">

This means, that when I select the war task, ant will :

  1. clean my build directory
  2. copy non java file
  3. compile
  4. build the war file

In the specified order. Thus I’m sure that any class file of the war file has been built from the last version of its source file. Consistency of source and binary is insured by the process. Notice that in some configuration, the cleaning task may be an option left to the developer. I definitely reject this option as my goal is consistency and not speed.

Exclude some java files from compilation

Your project may contains jUnit java test files. Obviously, this file are not needed in production. But they may even not compile as they need some specific jar files (jUnit jar for example).

A good habit is to group all junit java test in one package distinct from other source : in my case there where all sub package of fr.natsystem.myproject.test.

In the property part of my build.xml file, I‘ve defined the following property :

<property name="junit.package"  value="fr/natsystem/myproject/test/**" />

This property is used in the compile task :

<target name="compile" depends="clean,copy-context-delivery,copy-non-java-files">
    <echo>jUnit Package ${junit.package}</echo>
    <javac srcdir="${source-directory}" destdir="${classes-directory}" classpathref="project-classpath" excludes="${junit.package}"/>
</target>

The excludes property in the javac tag is expecting a relative exclusion path starting at the srcdir (src in our case). That means, it will be the package description where we replace dot by slash.

The last two stars (**) means that all subdirectories and there content are excluded. If you put just one star, you are limiting yourself at files in the specified directory.

Replacing a line in a config file with ant

In my case, I need two versions of my hibernate.cfg.xml file. The only difference was the DTD declaration at its beginning :

For production as I do not have an internet access :

<!DOCTYPE hibernate-configuration PUBLIC  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

In development, as when I trigger my test from eclipse the file is not found at this position, I opted for the URL

<!DOCTYPE hibernate-configuration PUBLIC  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "../webapps/myProject/hibernate-configuration-3.0.dtd">

hibernate.cfg.xml files are quite long and specific files that evolves quite a lot in the process of a development. I do not want to have to keep track of this change in two files.

The solution was to have a master hibernate.cfg.xml file called in our case hibernateAntMaster.cfg.xml. This file is the one that will be modified by developers. The DTD declaration is a tag that we will replace by an ant task.

<!DOCTYPE hibernate-configuration PUBLIC  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "@@DTD-Declaration@@">

We add two tasks in the ant build.xml file :

  • clean-context : this task will delete the actual hibernate.cfg.xml file and copy the reference hibernateAntMaster.cfg.xml as hibernate.cfg.xml. At this point we will have two identical files
  • copy-context-delivery : this task we replace the line in the hibernate.cfg.xml file. This task will do some other context specific tasks that we will see later.

The second task, will put the hibernat.cfg.xml file with the correct DTD declaration.

We use a property dtdLocation that we will specify in the build.properties file.

<target name="clean-context" description="Prepare files that needs to be modified depending on environment">
    <delete file="${source-directory}/hibernate.cfg.xml"/>
    <copy file="${source-directory}/hibernateAntMaster.cfg.xml" tofile="${source-directory}/hibernate.cfg.xml"/>       
</target>

<target name="copy-context-delivery" depends="clean-context" description="Task that updates files for the target environment">
    <replace file="${source-directory}/hibernate.cfg.xml" token="@@DTD-Declaration@@" value="${dtdLocation}"/>
    <delete file="${web-directory}/META-INF/context.xml"/>
    <copy file="${web-directory}/META-INF/${contextFile}" tofile="${web-directory}/META-INF/context.xml"/>
</target>

The replace ant tag allow to define :

  • the file you want to change
  • the token you want to replace in the content of the file : in our case we use @@ to delimit it in a way that avoid collision with existing syntax.
  • the value that will be used to replace the token in the file.

Picking the right config file with ant

We have two different versions of our context.xml file :

  • one for production
  • one for development

This file have little in common. They don’t evolve a lot and modification may be specific to only one of the file.

We just have to use one or the other. In our case, we create 3 files in WebContent/META-INF :

  • context.xml : the file used in test or production
  • contextProd.xml : the version we want to use in production
  • contextDev.xml : the version we want to use in development.

To achieve, the task we use the same copy-context-delivery task seen before :

<target name="copy-context-delivery" depends="clean-context" description="Task that updates files for the target environment">
    <replace file="${source-directory}/hibernate.cfg.xml" token="@@DTD-Declaration@@" value="${dtdLocation}"/>
    <delete file="${web-directory}/META-INF/context.xml"/>
    <copy file="${web-directory}/META-INF/${contextFile}" tofile="${web-directory}/META-INF/context.xml"/
>
</target>

First we delete the context.xml file, then we copy the right context file as context.xml file.

We use a contextFile property defined in the build.properties file.

Excluding directories in the ant war task

NatJet project have a directory resources in the WebContent directory. This directory contains the xml description of the panels. These descriptions are not used in production : they are transformed as java abstract class for execution.

We want to exclude this directory from the war file. The war file is build in ant from a build directory that contains classes and a WebContent directory that will be the root of the archive. We want to be able to use directly the WebContent directory at it is the rule. We do not want to have to build a second temporary directory.

To achieve, this goal we used the exclude tag to get the following definition :

<target name="war" depends="compile">
    <mkdir dir="${build-directory}" />
    <delete file="${build-directory}/${war-file-name}" />
    <war warfile="${build-directory}/${war-file-name}" webxml="${web-xml-file}">
        <classes dir="${classes-directory}" />
       
        <fileset dir="${web-directory}">
            <!-- Need to exclude it since webxml is an attribute of the war tag above -->
            <exclude name="WEB-INF/web.xml" />
            <!-- Exclude NatJet xml files for the description of panels -->
            <exclude name="resources/**" />
            <excludesfile/>
        </fileset>
        <manifest>
            <attribute name="Built-By" value="${builder}" />
            <attribute name="Built-On" value="${build-info.current-date}" />
            <attribute name="Built-At" value="${build-info.current-time}" />
        </manifest>
    </war>
</target>

Notice the manifest section : this will build a MANIFEST.MF file that will be in the META-INF directory of the war file. This allows to keep track of the version or the build number of the war.

Ant task to configure development environment

You’ve noticed that we have to deal with two environments : production and development. Two files (context.xml and hibernate.cfg.xml) depends on the selected environment.

We build the default of our ant build.xml and build.properties file to be the production environment. This is consistent with our main goal : integration for a customer delivery.

The process may affect the eclipse development environment. Thus as we have the ant infrastructure configured for our project it is quite straightforward to add a little task to configure the development environment.

For this we duplicate the copy-context-delivery ant task into copy-context-dev. The task will do the exact same thing, but we do not want to go through picking or modifying a build.properties file we replace properties by their direct definition.

In our case, it was :

  • the value in the replace tag
  • the filename of the file to copy as context.xml file
<target name="copy-context-dev" depends="clean-context" description="Task to set the development environment, should be used to update modification of master files">
    <replace file="${source-directory}/hibernate.cfg.xml" token="@@DTD-Declaration@@" value="http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"/>
    <delete file="${web-directory}/META-INF/context.xml"/>
    <copy file="${web-directory}/META-INF/contextDev.xml" tofile="${web-directory}/META-INF/context.xml"/>
</target>

In development, to set your environment you just pick this task and then do a refresh (F5) on the project node in your project explorer.

When you modify the hibernateAntMaster.cfg.xml, you just need to run this ant task to update the hibernate.cfg.xml file.

Notice, that this approach works fine, because we usually keep development environment quite consistent among the development team and we do not need specific configuration for each developer.

Summary : the complete files

We have two files both on the root of the eclipse project at the same level :

  • build.xml
  • build;properties

To edit build.xml, it may be better for you to use the right click : Open with and pick up the Ant Editor menu. This will bring syntax coloring and code completion.

To run the task, just right click on build.xml and select the menu Run As –> Ant Build… : this will opens a dialog where you can pick up the task : either choose war if you want to build the war files for delivery, or copy-context-dev to set the development environment or refresh the hibernate.cfg.xml or context file after a modification od the corresponding master.

build.properties file

The properties specific to the project or the environment will be set in this file.

Follow, the complete file:

project-name = myWarName
tomcat-home = C:/NatJet4.0.0/thirdparty/apache-tomcat-6.0.20
builder=Nat System
#Specific environnement propeties
dtdLocation="../webapps/myProject/hibernate-configuration-3.0.dtd”

contextFile=contextProd.xml

The project-name will be the name of the war file.

build.xml file

Follow the complete file :

<?xml version="1.0" encoding="UTF-8"?>
<project name="my-natjet-project" basedir="." default="war">

    <property file="build.properties" />

    <!-- Define Web Project Environnement -->
    <property name="source-directory"  value="src" />
    <property name="classes-directory"  value="build/classes" />
    <property name="web-directory"  value="WebContent" />
    <property name="war-file-name" value="${project-name}.war" />
    <property name="web-xml-file" value="${web-directory}/WEB-INF/web.xml" />
   
    <!-- Allow to exclude jUnit testcase-->
   <property name="junit.package" value="fr/natsystem/myproject/test/**" />
   
    <fail message="You need to specify contextFile property" unless="contextFile"/>
   
    <tstamp prefix="build-info">
        <format property="current-date" pattern="d-MMMM-yyyy" locale="fr" />
        <format property="current-time" pattern="hh:mm:ss a z" locale="fr" />
        <format property="year-month-day" pattern="yyyy-MM-dd" locale="fr" />
    </tstamp>
    <property name="build-directory" value="build" />

    <path id="project-classpath">
        <fileset dir="${web-directory}/WEB-INF/lib" includes="*.jar" />
        <fileset dir="${tomcat-home}/bin" includes="*.jar" />
        <fileset dir="${tomcat-home}/lib" includes="*.jar" />
    </path>

    <target name="clean">
        <delete dir="${classes-directory}" />
        <mkdir dir="${classes-directory}" />
    </target>

    <target name="clean-context" description="Prepare files that needs to be modified depending on environment">
    <delete file="${source-directory}/hibernate.cfg.xml"/>
    <copy file="${source-directory}/hibernateAntMaster.cfg.xml" tofile="${source-directory}/hibernate.cfg.xml"/>       
</target>

<target name="copy-context-delivery" depends="clean-context" description="Task that updates files for the target environment">
    <replace file="${source-directory}/hibernate.cfg.xml" token="@@DTD-Declaration@@" value="${dtdLocation}"/>
    <delete file="${web-directory}/META-INF/context.xml"/>
    <copy file="${web-directory}/META-INF/${contextFile}" tofile="${web-directory}/META-INF/context.xml"/>

    </target>
   
    <target name="copy-context-dev" depends="clean-context" description="Task to set the development environment, should be used to update modification of master files">
    <replace file="${source-directory}/hibernate.cfg.xml" token="@@DTD-Declaration@@" value="http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"/>
    <delete file="${web-directory}/META-INF/context.xml"/>
    <copy file="${web-directory}/META-INF/contextDev.xml" tofile="${web-directory}/META-INF/context.xml"/>

    </target>
   
    <target name="copy-non-java-files">
        <copy todir="${classes-directory}" includeemptydirs="false">
            <fileset dir="${source-directory}" excludes="**/*.java" />
        </copy>
    </target>

   
    <target name="compile" depends="clean,copy-context-delivery,copy-non-java-files">
        <echo>jUnit Package ${junit.package}</echo>
        <javac srcdir="${source-directory}" destdir="${classes-directory}" classpathref="project-classpath" excludes="${junit.package}"/>
    </target>

    <target name="war" depends="compile">
        <mkdir dir="${build-directory}" />
        <delete file="${build-directory}/${war-file-name}" />
        <war warfile="${build-directory}/${war-file-name}" webxml="${web-xml-file}">
            <classes dir="${classes-directory}" />
           
            <fileset dir="${web-directory}">
                <!-- Need to exclude it since webxml is an attribute of the war tag above -->
                <exclude name="WEB-INF/web.xml" />
                <!-- Exclude NatJet xml files for the description of panels -->
                <exclude name="resources/**" />
                <excludesfile/>
            </fileset>
            <manifest>
                <attribute name="Built-By" value="${builder}" />
                <attribute name="Built-On" value="${build-info.current-date}" />
                <attribute name="Built-At" value="${build-info.current-time}" />
            </manifest>
        </war>
    </target>

</project>

Conclusion

The two files can be used quite easily with any NatJet project. You may need to adapt or delete the clean-context and copy-context-delivery. I will recommend to put in comment their content in a first step because I’m almost sure that at one point you will have to deal with the same problematic. The other parts can be used as there are.

Thursday, July 1, 2010

How to follow a NatJet site with Google Analytics

Google Analytics is a standard free tool to follow activity on a web site.

This tutorial requires a Google Analytics account which is free.

Create a Google Analytics ID for your site

For a Google Analytics account you can create several profiles : a profile can be linked to a web site.

When you are logged in, you should see a link on the right part : ‘+Add a new profile’ (‘Ajouter un profil’ in french). Click on this link to create a new profile.

In this account you need to add a profile, on a single domain (this is the default)

You enter the URL of your site for example www.mycompany.fr:8080/MyNatJetApp

Google Analytics gives you some HTML to add in your head tag.

 

<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', ‘XXXXXXXXXX’]);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

</script>

This code is enough for most of the sites. When it comes to a NatJet site this is only a start, because this code is executed only when you load the page : in a NatJet app, you just have one HTML page (corresponding to only one URL).

Hopefully, Google Analytics provides you with several API that will be very useful in our case. I will focus on only two of them :

  • _trackPageview : This function allows you to send to Google Analytics the information  that a page have been loaded. Its parameter is the URL : thus in the case of a NatJet application, you can call this function at the end of the InitFormEvent with a parameter that can be the name of your panel.
  • _trackEvent : this function is dedicated to Ajax site (a NatJet application falls in this category) : It allows to track action from a user on a page. In a NatJet application, a user’s action may not change the page but you may still want to track this action : A simple case may be to know if people Save data or use a Print button.

Basic change to a NatJet project

For the demo, you will create a project MyNatJetApp (this should match the name you declare in Google Analytics).

With this NatJet project we will guide through the steps that will transform your NatJet application in a site that can be followed with Google Analytics : There are 3 mains steps (I do not count the project creation) :

  1. create a javascript file corresponding to the Google Analytics code that we want to add
  2. create a servlet that will upload the content of your javascript file in the main NatJet page
  3. update the web.xml file to use the new servlet

Create the NatJet Project

In NatJet select menu File –> New –> Project Wizard…

Just enter in the dialog, MyNatJetApp in Project Name and push Finish button.

The sample project is created.

Create the Google Analytics Javascript file

In the package fr.natsystem.natjet.app.MyNatJetApp, we are going to create a new js file : GoogleAnalyticsMyNatJetApp.js.

In NatJet select menu File –> New –> Other…

In the New dialog box, select node JavaScript and then JavaScript Source File. Press Next and enter the File name : GoogleAnalyticsMyNatJetApp.js

This creates a new file in . You can now add the Google Analytics code. You will add just the javascript part, without the html SCRIPT tag.

 

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', ‘XXXXXXXXXX’]);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

Notice, the XXXXXXXXX should be replaced by your own Google Analytics User Account.

Create a new servlet

The NatJet servlet needs to be overloaded. For this we will create a new servlet that will inherit from the standard NatJet servlet.

Menu File –> New –> Class

The package : fr.natsystem.natjet.app.MyNatJetApp

The name will be : NatJetAnalyticsServlet

The Superclass : fr.natsystem.natjet.echo2.webcontainer.WebContainerSpringServletNS

Check Constructor from superclass

The class is created. You need to add the following code :

package fr.natsystem.natjet.app.MyNatJetApp;

import nextapp.echo.webcontainer.Service;
import nextapp.echo.webcontainer.service.JavaScriptService;
import fr.natsystem.natjet.echo2.webcontainer.WebContainerSpringServletNS;

public class NatJetAnalyticsServlet extends WebContainerSpringServletNS {
       public static final String GOOGLEANALYTICS_JS_SERVICE_ID = "GoogleAnalytics.Script";
       private static final String GOOGLEANALYTICS_JS_SERVICE_URI = "/fr/natsystem/natjet/app/MyNatJetApp/GoogleAnalyticsMyNatJetApp.js";
       public static final Service GOOGLEANALYTICS_JS_SERVICE = JavaScriptService.forResource(GOOGLEANALYTICS_JS_SERVICE_ID, GOOGLEANALYTICS_JS_SERVICE_URI);

    public NatJetAnalyticsServlet() {
        super();
        addInitScript(GOOGLEANALYTICS_JS_SERVICE);
    }

}

This code indicates to the NatJet runtime, that it need to add the Script tag with the content of the GoogleAnalyticsMyNatJetApp.js file in the header of the NatJet page.

Update web.xml

We need to use the servlet we’ve just defined. Open web.xml in WebContent/WEB-INF and  replace in the servlet-class tag the existing servlet by the name of your servlet : fr.natsystem.natjet.app.MyNatJetApp.NatJetAnalyticsServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <display-name>MyNatJetApp</display-name>
    <servlet>
        <servlet-name>WebContainerSpringServletNS</servlet-name>
        <servlet-class>
            fr.natsystem.natjet.app.MyNatJetApp.NatJetAnalyticsServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>WebContainerSpringServletNS</servlet-name>
        <url-pattern>/app</url-pattern>
    </servlet-mapping>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    <session-config>
        <session-timeout>5</session-timeout>
    </session-config>
</web-app>

Now you can deploy your NatJet application on you web site : when somebody reach your application it will count for one visit on one page.

The demo application have only one page, but in the case of your final application you may have more than one panel and you may be interested in tracking which panel are used by visitors.

More tracking of a NatJet Application

Previous steps have allowed you to track visits, but you just know that you’ve got a visit : you don’t know which panel the visitor called and which actions he made.

We are going to use the two Google Analytics functions we introduced sooner but first we are will externalized the GA user account number.

Externalized a property

The GA user account is used every where, as it may changed it is better to extract its value in a configuration file.

NatJet creates for you a file nsUserContext.xml in WebContent/WEB-INF.

In this file you just add a new entry in the map section to get the following :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- <bean/> definitions here -->
<bean id="nsUserSettingsConfiguration" class="fr.natsystem.natjetinternal.application.PvConfigEntry$UserSettingsConfiguration">
    <property name="userSettingsConfigurationMap">
        <map>
            <entry key="googleAnalyticsUA" value="UA-XXXXXXX-X" />
         </map>
    </property>
</bean>
</beans>

Now you can get this value by using the following code :

NsTools.getUserConfigurationParameter("googleAnalyticsUA", "")

 

Tracking Panel Change in a NatJet application

When a new panel is displayed in NatJet, you can trigger a InitFormEvent. This event, will allow us to detect that a panel have been displayed.

We need a function to trigger a Javascript function of the Google Analytics API from your NatJet code.

This is possible by retrieving the Echo3 Application Instance : then you create a JavaScript command with the JSCommand object and you execute it by enqueuing this command. NatJet will trigger this javascript command after its own Javascript functions when all Java code will have been executed and the browser updated with the new panel.

Here is the code you need to add at the end of your InitFormEvent :

    E2AppInstance app = (E2AppInstance) NsAppInstance.getActiveFactoryAppInstance();
    if (app!=null) {
        app.enqueueCommand(new JSCommand("_gat._getTracker('"+NsTools.getUserConfigurationParameter("googleAnalyticsUA", "")+"')._trackPageview('/" + this.getName() + "')"));
    }

We use this.getName() to insert in the JavaScript command the name of the NatJet panel.

Thus before displaying this panel NatJet triggers this event : the code prepares a JavaScript command which is based on Google Analytics API.

When NatJet sends back its XML synchronization message to the browser, the message contains a part that say it will need to execute this Javascript function.

When the NatJet javascript runtime on the browser get the message, it update the display and then trigger the JavaScript code. This code informs Google Analytics that the page with the URL corresponding to the panel name have been loaded.

By repeating this method in each InitFormEvent of the panels you want to track you are now able to follow the visit on your NatJet site.

Tracking Activity in a NatJet Application

In a Ajax application as a NatJet one, activity is not always linked with the display of a new panel : you may be interested to know if a visitor add a record or delete record or save data….

They are actions triggered by a PushButton but they do not display a new panel, you need something different from the previous method.

This is where the Google Analytics function _trackEvent  is interesting.  This function accepts four parameters :

The first two are compulsory but their value is completely free : the way you want to track this actions will bring you to adopt your own naming convention.

The following example uses two parameters to track that a visitor used the Save pushbutton on a Candidat panel.

E2AppInstance app = (E2AppInstance) NsAppInstance.getActiveFactoryAppInstance();
if (app!=null) {
    app.enqueueCommand(new JSCommand("_gat._getTracker('"+NsTools.getUserConfigurationParameter("googleAnalyticsUA", "")+"')._trackEvent('Candidat', 'Save')"));
}

This is the same mechanism as described before, the only change is the Google Analytics function we are using.

To follow this activity on Google Analytics, you need to go in Content (Contenu in french) and then select Suivi des événements.

Conclusion

In this post we show you how to track the activity on a NatJet Application : we’ve tried to introduce you to a richer tracking system where you detect activity of your users.

Wednesday, March 24, 2010

Layout or how to build modern web application with NatJet ?

We saw in the previous post, that NatJet allows to build web applications similar in interactivity to classic Windows applications.

NatJet could allow to build classic MDI applications that runs in a browser : title bar, menu bar and drop down menus and popup windows… This will simulate the kind of windows application we used to have back in Windows 95.

Introducing Layout

Notice, that current native Windows applications do not use anymore this kind of behavior : Outlook, Eclipse… They all prefer avoiding opening popup windows. They split the screen in several zones : menu, toolbar, navigation (usually the left zone) and a main central zone (that display the main information).

If you look at advanced web application like GMAIL, Y! Mail or SalesForce you will find the same kind of layout. Web App tends also to avoid drop down menu.

When we’ve started NatJet we found out that customer tends to pick up the latest kind of screen organization. If you look at deeper this kind of application, you will notice that they have usually 3 zones :

  • Top Status zone that displays connection and general information
  • Left Navigation bar that contains a list of links
  • A main zone that display data

The first two zones do not change a lot and when they do, this is mostly a specific data (a label, a link…).

The main zone changes a lot : it can display a list of mails, then a detail of a mail, then another list. Data, Structure of data and type of data can change.

Usually, when you want to change this part of the screen, you need to load a new pages reloading the two zones that haven’t changed.

Furthermore, at development time you need to rely on template of pages (HTML, JSP or JSF) to keep consistent layout, but you need to duplicate import declarations in each pages even if you do not duplicate the code. The page you’ve built is not independent of the layout : if you change the layout, you will need to change the import declaration of all the pages.

Designing Layout in NatJet

Layout in NatJet allows a simple organization of the browser’s client area. With a graphical designer you split this area in several zones. By default, NatJet provides you with a layout composed of 3 panels.

Layout3Panels

In the upper screen copy, you can see

  • splitters : horizontal and vertical lines
  • containers : empty areas

You can design with the graphical designer complex layout : Have a look to the following prototype with 9 containers.

MultiPannelLayout

Container cannot contain controls : Controls have to be included in panels. A container, is just an empty zone that can contain a panel. Panel can change during the execution of the NatJet application. When NatJet replaces a panel by another one, it doesn’t send back all other panels. This is much more efficient.

Furthermore, if you look at development time, a panel doesn’t need to know where it will be displayed. If you decide to change the structure of your layout, you do not need to change your panel. The same panel can be displayed in different containers or in different layout.

How to have invisible splitter in NatJet

In the upper layout all splitter are visible. Sometimes, designer splits logically the browser area but doesn’t show the limit of each zone (this gives a more smooth layout)

InvisibleSplitter

In the upper layout, there are 3 panels :

  • top
  • left, with the “Recherche” zone
  • main

But, the designer doesn’t want to show the separation between the left and main panel.

Open the Layout3Panels.xml resources in its graphical editor.

You need to select the Splitter : this is very difficult in the graphical editor, I prefer to use the Outline : expand node Layout3Panels, than expand RootMainContainer, than TopHorizontal than, TopHorizontalSecond. At this point you should have a SplitLayout1.

OutlineLayout

If you select it, you should see in the Properties view that it is a Component of type SplitLayout (this is the technical name of a Splitter in NatJet).

SplitLayoutProperties

The best way to render invisible a splitter is to :

  • set Separable Movable to false : this means you won’t be able to change the position of the splitter at execution time which can sound rational if it is no more visible.
  • set Separator width = 0

It can be safe to select the same Separate Color (the color of the splitter) as the one of your NsContentPane background (222,222,221 by default)

Chaining Layout in NatJet

A layout is a kind of organization of your screen. in some apps you may need several organizations of your screen : you may start with a very simple layout with just one panel (for login by example) than move to a more traditional 3 panels layout.

To illustrate the end of this post, we will create a new NatJet Project : NatJetLayout.

  1. Just select in menu File –> Project Wizard.
  2. Then input in the Project name : NatJetLayout
  3. Then press the “Finish” button.

This will create your NatJet project.

Creating a new layout

The first step will be creating a new layout. This is fairly simple :

  1. Select in the Package Explorer, the node WebContent –> resources –> NatJetLayout of your NatJetLayout project. This part is important to let know at NatJet where it should add the new resource.
  2. Select menu File –> New –> Form Wizard. The “New Form” dialog box pops up.
  3. Enter the Form name : SimpleLayout
  4. Select the Radio button : Layout
  5. Press the “Finish” button

CreateLayout

A new resource has been added to your NatJetLayout node : SimpleLayout.xml. If you double click on it, it will be opened in the NatJet graphical designer.

The default Layout is composed of just the container corresponding to the full client area browser : RootMainContainer (Notes this is a new default in NatJet 4.0, it may be slightly different in NatJet 3.0).

A container (the NatJet technical name is LayoutContainer) can get either a pane or a SplitLayout. The pane allows the attachment of a panel and the SplitLayout allow to split the container in two new containers.

As we are just interested in chaining layout, we will just add a pane and set its Pane property to the MainPanel. For this :

  1. drag and drop from the Palette view, the Pane resource.
  2. Select the Pane resource you’ve just added
  3. In the Properties view, click on the Pane property. A button with 3 dots (“…”) shows up
  4. Click on the button “…”, a dialog box “Chosse a content pane” is opened
  5. Type MainPanel, it will present you with the ressource “MainPanel – fr.natsystem.natjet.app.NatJetLayout.ui”
  6. Select it and press “OK” to validate your choice

You’ve just attached a default panel to this container. Now, when the layout will be built at execution time, it will pick up this panel to fill the container. You can change by programming this panel, but if you don’t do anything it will pick up this one.

Changing the browser Title bar in NatJet

When you run the default NatJet application, you will get a title bar of your browser showing : Layout3Panels or any name you choose for the name of your layout.

To change it, this is very easy : when your layout is opened, select in the Outline view the first node, in our case the SimpleLayout node. This actualizes the Properties view.

At its bottom, you have a Caption property which is by default empty. You can enter anything you want, like : My first layout in NatJet.

You can save and close the layout SimpleLayout.

Start your NatJet application on a new Layout

By default, a NatJet application start with the Layout3Panels it creates when it creates a new project.

In our case, we want it to start on the SimpleLayout layout. To do this, we need to change the spring bean “mainLayout” that is used to create the first layout.

In NatJet 4.0, this bean is defined in the file natjetInstanceContext.xml of the node WebContent –> WEB-INF. To open it, click right on it and select Open with –> Text editor.

In the file, change Layout3Panels by SimpleLayout. Be careful to specify the complete name with the correct package.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <!-- <bean/> definitions here -->
    <import resource="ContextBean/emptyContextBean.xml" />
    <bean id="applicationInstance" class="fr.natsystem.natjet.app.NatJetLayout.NatJetLayout"
        scope="prototype">
        <property name="context">
            <ref bean="natjetSessionContext" />
        </property>
    </bean>
    <bean id="mainLayout" class="fr.natsystem.natjet.app.NatJetLayout.ui.SimpleLayout"
        scope="prototype">
    </bean>
</beans>

Save your file.

If you run the NatJet application, you will have a simple layout with a title bar of the browser saying : “My first layout in NatJet”

Display a new Layout

We want from the first layout (SimpleLayout) to allow the user to trigger an action that will display the second layout (Layout3Panels).

The layout SimpleLayout displays only the panel MainPanel. We are going to edit it to add a pushbutton.

  1. Double click on MainPanel.xml to edit it in the NatJet designer.
  2. Drag & drop a PushButton, and change the following properties :
    • name : displayLayout3PanelsPB
    • caption : Display 3 panels
  3. Drag & Drop a second PushButton and change the following properties :
    • name : displaySimpleLayoutPB
    • caption : Display Simple Layout
  4. You can change its width to allow it to display the full caption.

Its times to code : click on the event Executed of one of the two button, the button “->” shows up. Click on it, validate the message to save the panel.

You can go, on the class definition in the java code and click left on the error (the small red circle with a cross), select “Add unimplemented method”, this will add all method corresponding to Executed action on your pushbutton.

Then complete the code as following :

@Override
public void displaySimpleLayoutPB_ExecutedEvent(NsExecutedEvent arg0){
    NsRootLayout.getRootWindow().getLayoutContainer().display(0, SimpleLayout.class, null);
}

@Override
public void displayLayout3PanelsPB_ExecutedEvent(NsExecutedEvent arg0) {
    NsRootLayout.getRootWindow().getLayoutContainer().display(0, Layout3Panels.class, null);
}

The main function used :

  • NsRootLayout.getRootWindow() : this static method retrieves the client area of the browser. This is the root of the display of a NatJet web application.
  • getLayoutContainer : gives you the Container in which the layout is displayed : the empty container corresponding to the browser client area.
  • display() : this is the method to display a layout

Save your file.

You can run your web application and use the two buttons to move from one layout to the other.

Conclusion

Layout in NatJet is an interesting feature :

  • It allows to keep a consistent look through a complete application.
  • This optimizes exchanges between browser and server.
  • Layout eases application development : once the layout is done, developer can focus on panels

Layout does not prevent you from using popup windows. In our case we tend to limit them to simple dialog box with a little information to input (less than 5 fields).

Sunday, March 14, 2010

Introducing NatJet a 4GL to build Rich Interface Application

This first post will, through a simple Hello World tutorial, give a quick presentation of NatJet.
NatJet is a a 4GL to build Ajax Applications in which you code in Java. This means basically 3 things :
  • Your only programming language is Java
  • 4GL means that you will get a productivity close to the one’s of Visual Basic.
  • This is a web application that will give your interface the interactivity of a traditional Windows application
Notes that though this is an unofficial blog that does not reflect the official position of Nat System (Editor of NatJet), as product manager of NatJet I may have some times a bias position about it. Nevertheless I will always try to be objective and technically precise and impartial.

NatJet as Eclipse Plug-In : Create a new project

NatJet is a plug-in of Eclipse. NatJet 3.0 is based on Eclipse Ganymede (3.4.2) and NatJet 4.0 will be probably based on Eclipse (3.5.1).
This plug-in extend WST : This means that any NatJet project will be a Dynamic Web Project with the same file structure :
  • src : this repository will contains Java sources, properties files used for internationalization and the xml file used to define style.
  • build : where class files are generated when java classes are compiled
  • javadoc : where you will find the NatJet Javadoc
  • lib : this repository contains jar files used only in development stage useless in the production stage as Junit.
  • WebContent : this repository contains the jar files that constitute the NatJet runtime, the web.xml file, the xml files used by Spring and the xml files used by NatJet to store page descriptions.
A dynamic web project, is a web project that runs behind a Java Application server that allow to build html pages dynamically (to be opposed to static html pages) that can display data accordingly to user input.
A project based on Servlet, JSP, JSF… are dynamic web projects.

Select NatJet Perspective

The first step after NatJet installation (just double click on the NatJet msi) is to select the NatJet perspective :
Select menu : Window –> Open Perspective –> Other…
In the “Open Perspective” dialog, select NatJet and press OK.

Create a Server

Dynamic Web Projects rely on a web server. The first step will be to create a Server definition in Eclipse.
Select menu : File –> New –> Other…
In the “New” dialog box, select folder Server and then node Server. Press button “Next”.
In the “New server” box, you select the folder “Apache” and then “Tomcat V 6.0 Sever”. When you install NatJet a folder Thirdparty is installed as subfolder of your NatJet repository. In this folder you will find another folder “apache-tomcat-6.0.18” that contains a Tomcat install that you will be able to use.
Press “Next >” button, you will need to browse the Tomcat installation directory.
Note that you can used any other server already installed on your computer.

Create a NatJet Project

Now that the NatJet perspective is open, you will be able to use some NatJet menu.
Select menu : File –> New –> Project Wizard.
In the “New Dynamic Web Project” dialog you can enter the project name : NatJetHelloWorld
CreateProject
You have to pick up as Target Runtime, the server you want to use and that you’ve hopefully created in the previous step “Create a Server”
You can press Finish. The project has been created.

Run the application

You can click right on the project node in the “Package Explorer” and select in the popup menu “Run As” –> “Run on server”.
This menu, is part of the JEE version of Eclipse, it deploys the corresponding war of your project on the server and start the server (in our case, an Apache server), then it start the embedded browser of Eclipse (this browser behaves like a Internet Eclipse 7.0) on your application.

Be careful, the page it calls, depends on the node on which your click. If you want to call the right NatJet page, you need either to be on the Project Node or in the index.html page in the WebContent node of your project.
You will get the standard 3 panels layout NatJet created as starting point of a project.
EmptyProject

NatJet an Eclipse plug-in : conclusion

This first part has shown that a NatJet project is a WST Eclipse project : you will be able to use the same function as any WST project like :
  • deploying and executing your project on the web server of your choice
  • debugging on the web server
You will also be able to use other Eclipse plug-in like CVS or SVN for source management or any other functionality as far they are compatible with a JEE Eclipse version.
NatJet has it own perspective that adds some useful menus and views to work with.
To finish, you have to know that the NatJet plug-in is the folder : C:\NatJet3.0.2\eclipse\plugins\fr.natsystem.natjet.graphicaldesigner_3.0.2.20100304a.
All you need is this folder in the Eclipse\plugins folder to add NatJet functionality to any Eclipse.
Starting with version 4 and over, there will be probably several folders fr.natsystem.natjet in the plugins folder.

NatJet Hello World Tutorial

Usually I’m not fond of Hello World tutorial as they don’t show any interesting feature that will be really useful in the real life.
Nevertheless I will bend myself to the request to show 2 important behavior of NatJet :
  1. absolute positioning of graphical control
  2. high interaction of the TextField control
In the HelloWorld project created in the previous chapter we will add a TextField control and a PushButton.

Layout Layout3Panels.xml

All graphical resources (we call them Layout or Panels in NatJet) are in the subfolder resources of the WebContent folder.
You should have there a NatJetHelloWorld folder with a Layout3Panels.xml file inside (with other xml files).
NatJet organizes graphical resources in folders that it calls Module. You can create as many modules you want.
When you create a new project, NatJet creates for you a default module with the name of your project (in our case NatJetHelloWorld). In this module, it adds several NatJet resources to give you a starting point for your project.
One of this resource is a basic layout : a layout is for NatJet a basic organization of the browser page. You can subdivide a page in as many subpart you want. By default, NatJet provide you with a typical layout in 3 parts :
  • top status bar
  • left navigation bar
  • main part that will show data
This layout is the Layout3Panels.xml you can find in the NatJetHelloWorld folder. If you double click on it, it will open in the NatJet Layout Editor
Layout3Panels
In the image, you can see the 2 splitters : one horizontal and one vertical and the 3 zones.
Zones are called in NatJet : container. They contain Panel. The panel is the graphical object in which we can add controls as TextField and PushButton.
In the editor, you can move the splitter, you can add new splitter, you can use the properties view to modify some of the properties.
Note, in our case, you don’t need to do anything : it was just to illustrate the two notions of layout and panel.
Close the Layout.

Edit MainPanel.xml

In the NatJetHelloWorld folder, we are going to edit the panel MainPanel.xml. Double click on the file.
The Panel editor opens the file and shows an empty panel. You should have on the right part of eclipse a Palette view. This view will allow you to add controls by drag & drop on your panel.
We will add :
  • a Label
  • a TextField
  • a PushButton
MainPanelEditor
Select the Label1 control by clicking on it. Then select the view Properties on the left part of Eclipse.
You should have a Caption property, in which you can enter the new caption you want to see : “Please, enter your name”
LabelProperty
As you press Enter, the Label1 in the graphical editor is replaced by your new text. If your text goes over the textfield, you can grab the text or the textfield to move it.

TextField basic properties

Now we going to define the behavior of the input textfield. Select the control TextField in the graphical editor, the Properties view shows its properties.
You can grab one side of the TextField to reduce its width by drag and drop.
You can alternatively use the width property of the editor.
We’re going to precise the following properties :
  • Name=nameTF : we give the control a meaningful name to be able to manipulate it by programming later.
  • Regex Type=alphabetical : this is a very interesting behavior of NatJet TextField : This is a quite complex set of characters that are allowed as input. Any other characters won’t be input. We don’t means that the control will show an error, we mean that the input will be rejected. This is what I call maximum interaction with the user : if the character is forbidden, it won’t be even allow to enter it. This is a quite basic functionality on Windows application. In the case of the alphabetical set, is a complex set of characters that should correspond to what you need to enter a name : Upper and lower case, accented characters (yes ! we are French), single quote, space and dot. I believe this feature to be very helpful : Most of text input will be fine with this rule and will prevent intrusion of dangerous characters.
  • Upper case mode=true : this will force the input in uppercase. I’m aware that this feature may not be very useful in a Hell World, but this kind of feature that allows a careful controlled and calibrated input of some important information (like name) is very important in real life application
TextFieldProperty

PushButton and action

This is time to display our Hello World message according to the input.
Select the PushButton and enter the following properties :
  • Name=displayPB : name of the control. This will ease programming.
  • Caption=Say hello : text that will appear on the PushButton
If we click on the property Event –> Executed, an arrow will appear on the right.
PropertyPB
If you press the button “->”, you will get a message asking you to save the modification on your file. Answer “Yes”. NatJet, will bring you directly on the java class corresponding to your panel.

Responsibility and separation of concerns in NatJet

When you drag and drop controls in the NatJet Designer, NatJet saves this description in an XML files. This file describes the Presentation layer and only this part. If you press on the Source tab on the bottom of the Graphical Designer, you will find the source of this file and you will not find any Java code. This is pure presentation layer.
From this description, NatJet generates automatically an abstract java class corresponding to this file. This abstract java class (in our case : fr.natsystem.natjet.app.NatJetHelloWorld.ui.api.AbstractMainPanel) contains java code that will build the presentation layer.
NatJet generates also a java class that inherit from the previous abstract class. This java class (in our case fr.natsystem.natjet.app.NatJetHelloWorld.ui.MainPanel) contains methods that will be triggered by NatJet accordingly to the user action.
The developer will code this method to interact with the user. In these methods, the developer will have available all controls as properties of this java class.There is no technical code in this java class. NatJet enforces a clear separation between the generated code (only in the abstract class) and your code (only in the java class).
For example, we will complete the method displayPB_ExecutedEvent (note the name of the PushButton as first part of the method name).
  1. In this method we will retrieve the value input by the user : nameTF.getText()
  2. And we will use the msgBox service to display a message to the user.

The final code, will be :

package fr.natsystem.natjet.app.NatJetHelloWorld.ui;
import fr.natsystem.natjet.app.NatJetHelloWorld.api.*;

import fr.natsystem.natjet.app.NatJetHelloWorld.ui.*;

import fr.natsystem.natjet.app.NatJetHelloWorld.swt.*;

import fr.natsystem.natjet.app.NatJetHelloWorld.ui.api.*;

import fr.natsystem.natjet.app.NatJetHelloWorld.*;

import fr.natsystem.resource.localization.NatJetHelloWorld.*;

import fr.natsystem.natjet.event.*;

import fr.natsystem.natjet.window.*;

import fr.natsystem.nslogger.NsLogger;

import fr.natsystem.natjet.exception.*;

public class MainPanel extends fr.natsystem.natjet.app.NatJetHelloWorld.ui.api.AbstractMainPanel {

 

    public MainPanel(fr.natsystem.natjet.window.NsLayoutContainer arg0, java.lang.Object arg1) throws fr.natsystem.natjet.exception.ENsUnableToRemoveForm {

     public Object getUserData(){

         return null;

     }


    @Override

     public void setUserData(Object arg0){     }


    @Override

     public void displayPB_ExecutedEvent(NsExecutedEvent arg0){

         msgBox("Hello "+nameTF.getText(), "Message");

     }


}

Positioning and Alignment of controls

We will finish back with the Graphical Designer.
NatJet uses absolute positioning for controls : Each control have x,y coordinates. When you drag & drop a control you move it around changing its coordinates.
This gives you a great freedom and precision to build your display : this point maybe quite useful when building complex display with lot of controls.
You have plenty of option to handle alignment :
  1. You can used the Marquee to select several controls with a lasso
  2. You cans use in the toolbar the Align Middle button align selected controls
Notice, that you have several different kind of alignment options in the toolbar.
Alignment

Run the application

If you stopped the server, you can restart it and see the finished application running.
When you enter a name, notice that if you try to input a number nothing happens.

If you type a lower case, it is automatically transform in upper case.
When you press the button, you get a message box.
ExecMsgBox

Conclusion

This is a very quick glance to NatJet possibilities. I’ve tried to stress several points that seems to me very important :
NatJet is a Eclipse plug-in that allows you to enjoy all possibilities and functionalities of Eclipse
With NatJet is very easy to build elaborate and very interactive displays : No Ajax, no Javascript or HTML knowledge are necessary and yet we are able to reject some character or transform input.
NatJet is based on absolute positioning of controls to give developer flexibility
NatJet is like modern development tools for Windows like VB or our former product NS-DK or NatStar : Drag & Drop of controls and a property view to parameterize them.