Sunday, December 25, 2016

How To Get Feature Name And Scenario Name Using Cucumber-JVM

Sometime we need to know the Feature name or Scenario name whenever we ran our script using Cucumber-JVM. It may be required for logging purpose for status of execution. So How we can do that. It is very simple we need to add the following lines to get the Feature Name and Scenario Name.

"Feature Name : " +  scenario.getId().split(";")[0]
"Scenario Name : " + scenario.getName()

Wednesday, November 30, 2016

How to Pass Examples Data As An Object In A Step Method Using Cucumber JVM

Still now whenever we use Scenario Outline we need to pass all parameters in a step method those are mentioned in glue code. So problem is if Examples section of Scenario Outline holds more parameters and also if our steps in feature file holds all the parameters then method argument count should be increased.

Now if we pass all parameters as a object then it becomes a single parameter on step method even if our feature steps holds lots of parameters. But there is a trick to make all this parameters as an object in step methods those are mentioned in feature steps.

So basically we mentioned our Example data to the feature step as Data Table.

So as an example :

Feature: This is a sample feature file

  Scenario Outline: This is a scenario to test datadriven test on Cucumber JVM.
    Given scenario data
    When executed from Runner Class.
    Then UserName and Password shows on console from Examples "<username>" and "<password>"
    Then UserName and Password shows on console with header as:
      | username     | password     |
      | "<username>" | "<password>" |
    Then UserName and Password shows on console without header as:
      | "<username>" | "<password>" |

    Examples: 
      | username        | password        |
      | Test UserName 1 | Test Password 1 |
      | Test UserName 2 | Test Password 2 |

So Glue Code is 

@Then("^UserName and Password shows on console from Examples \"([^\"]*)\" and \"([^\"]*)\"$")
public void usernameAndPasswordShowsOnConsoleFromExamplesAnd(String userName, String password) throws Throwable {
System.out.println("UserName : " + userName + " Password : " + password);
}

@Then("^UserName and Password shows on console with header as:$")
public void usernameAndPasswordShowsOnConsoleWithHeaderAs(List<ExampleData> data) throws Throwable {
System.out.println("UserName : " + data.get(0).getUsername() + " Password : " + data.get(0).getPassword());
}

@Then("^UserName and Password shows on console without header as:$")
public void usernameAndPasswordShowsOnConsoleWithoutHeaderAs(List<String> stringData) throws Throwable {
System.out.println("UserName : " + stringData.get(0) + " Password : " + stringData.get(1));
}

class ExampleData{
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}


}

 So if we drill down our code we can observe that when we mentioned the header of Examples below the feature steps we need to Create data object class in glue code to use it.

If user don't want to create any dataclass for that then user cannot mention the header name below the steps.



  

Sunday, October 9, 2016

Passing Parameter In Maven

Maven also provide us the facility to pass parameter at runtime and we can use this property value from our Java code.

There  are several way to get properties value from maven. Here we will learn how to retrieve the System property value from maven.

We use "maven-surefire-plugin" to set System Property from pom.xml and then it is very easy to retrieve system property using java.

So our sample pom.xml is looks like below :

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<systemPropertyVariables>
<testproperty>tetsproppertyValue</testproperty>
</systemPropertyVariables>
</configuration>
</plugin>
Sample code example :
public class TestLocalRepoTest {

@Test
public void sampleTest(){
System.out.println("Test System Property Property Value From Maven :"+ System.getProperty("testproperty"));
}
}

if we want to run this main class from maven we need to type this command

test

We can also access the System Properties if we run the main java class from maven. Then we need to use "exec-maven-plugin" to set the System Propertiey

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${exec.mainClass}</mainClass>
<systemProperties>
<systemProperty>
<key>testproperty</key>
<value>tetsproppertyValue</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>

Sample code example :
public class TestLocalRepo {

public static void main(String[] args) {
System.out.println("Test System Property Property Value From Maven :" + System.getProperty("testproperty"));
}
}

if we want to run this main class from maven we need to type this command
exec:java  -Dexec.mainClass="com.automation.example.TestLocalRepo"

Sunday, September 25, 2016

Adding External Jar In Maven Project

We know that maven is a build management tool. So when ever we need to add any jar for our project we can mention dependencies in POM(Project Object Model) file(xml file) then it automatically pull the jar from remote repository(default Apache Maven repository) and store it in default location(C:\Documents and Settings\User\.m2) if it is not available under this location.

But what happen if this jar is not available on remote server, but we need this jar to run our project. As well as we have no permission to upload this jar in any maven repository due to some security or restriction.

So maven provide us different way here we discuss two ways:
  1. Add jar in System Property.
  2. Use jar as project specific local Repository.
1. Add jar in System Property

For this we need to mention the path of this jar under <systemPath> tag and mention system under <scope> tag.

e.g :

<dependency>
    <groupId>group id name</groupId>
    <artifactId>artifact name</artifactId>
    <version>version number</version>
    <scope>system</scope>
    <systemPath>jar location</systemPath>
</dependency>


2. Use jar as project specific local Repository

For this we need to create a specific folder structure in our project as given below



and mention the repository path under <url> tag like file://${project.basedir}/lib
e.g :
        <dependency>
            <groupId>seleniumserver</groupId>
            <artifactId>seleniumserver-sdk</artifactId>
            <version>2.53.0</version>
        </dependency>
        <dependency>
            <groupId>seleniumclient</groupId>
            <artifactId>seleniumclient-sdk</artifactId>
            <version>2.53.0</version>
        </dependency>



    <repositories>
    <repository>
        <id>in-project</id>
        <name>In Project Repo</name>
        <url>file://${project.basedir}/lib</url>
    </repository>
    </repositories>


 




Tuesday, August 16, 2016

Running VBScript from Java

Some time we may require to run vbscript from java application. It is mainly required when we try play with system. So sample code is given below how to call vbscript file from java application.

public class TestVBScript {

    public static void main(String[] args) throws IOException {
        try {
            System.out.println(new File("a.vbs").getAbsolutePath());
            Runtime.getRuntime().exec("wscript.exe " + new File("samplevbs.vbs").getAbsolutePath());

        } catch (IOException ex) {

        }
    }
}

Saturday, July 16, 2016

Running batch file from Java

One of the most important requirement for batch file is to run the same command what we run from command line. If we type the series of command in normal file and save this file with .bat extension then this file converted to batch file. Above mentioned extension is only for windows machines for different operating machine batch extensions are different.

So now question is how to run a batch file from java ?

Sample snippet given below :

    public static void main(String[] args) throws IOException {
        try {
            String[] command = { "cmd.exe", "/C", "Start", "runTestNGScript.bat" };
            Process p = Runtime.getRuntime().exec(command);
        } catch (IOException ex) {
      }
    }


Sample batch file given below :
@echo off
echo Set JAVA Path

set PATH=%PATH%;G:\Program Files\Java\jdk1.7.0_79\bin

echo Now PATH is %PATH%


cd %cd%

echo Current Directory %cd%
echo Set Project Path
 
set ProjectPath=%cd%

echo Now project path is : %ProjectPath%


echo Set Class Path

set classpath=%ProjectPath%\bin;%ProjectPath%\lib\*

echo Now project Class path is : %classpath%

echo start compiling
 

javac test\com\automation\test\*.java -cp lib\* -d bin
 

echo Project compiled

echo run test class using TestNG
 

java org.testng.TestNG %ProjectPath%\testng.xml
 

echo Run completed
 

pause

Saturday, May 14, 2016

Custom Cucumber Reporting Using Maven

On previous post we learn how to create custom cucumber report using normal java project.

If we use this report for normal java project we create a custom annotation to generate this report, but if we use maven for this report then it is so easy to create the report and we need very less code.

So how we create this custom cucumber report using maven

Step 1:

 Open eclipse and go to this path File ->New -> Project... ->Maven -> Maven Project.

Click on next after select the Maven Project

Step 2:

 Check the  "Create a simple project (skip archetype selection)" and click on next.
Type the Group Id and Artifact Id under the Artifact section and click finish button.
Group Id means if any organization have multiple project under one group like here we group all cucumber projects under one group.

Artifact Id is the project name.

Step 3:

Our project structure now created in our eclipse work space and it looks like



Step 4 :

We create feature file and put it under src/test/resources.Cucumber runner as well as step file put into src/test/java after creating package. So it looks like


 Step 5 :

Create pom.xml file with all the details and run it using the below command. Here we need to remember that custom report only generated if we use verify or later sequence command from maven default life cycle. So we use install here.

clean install site



Step 6:

 After running this pom.xml report generated under target folder.






Complete Code :

Feature File:

Feature: Calculator
  As a user
  I want to use a calculator
  So that I don't need to calculate myself

  Scenario: Add two numbers
    Given I have a calculator
    When I add 2 and 3
    Then the result should be 4


Step File :

package com.selenium.cucumber.steps;
import org.junit.Assert;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

public class StepFile {
    int actualSum;

    @Given("^I have a calculator$")
    public void iHaveACalculator() throws Throwable {

    }

    @When("^I add (\\d+) and (\\d+)$")
    public void iAddAnd(int value1, int value2) throws Throwable {
        actualSum = value1 + value2;
    }

    @Then("^the result should be (\\d+)$")
    public void theResultShouldBe(int expectedSum) throws Throwable {
        Assert.assertEquals(expectedSum, actualSum);
    }

}

 

Runner :
package com.selenium.cucumber.runner;

import org.junit.runner.RunWith;
import cucumber.api.CucumberOptions;
import cucumber.api.SnippetType;
import cucumber.api.junit.Cucumber;

@CucumberOptions(plugin = { "html:target/cucumber-html-report", "json:target/cucumber.json" }, features = {
        "src/test/resources/featureFile.feature" }, glue = {
                "com/selenium/cucumber/steps" }, strict = true, dryRun = false, monochrome = true, snippets = SnippetType.CAMELCASE)

@RunWith(Cucumber.class)
public class CucumberRunner {
    public static void generateReport() {

    }
}


pom.xml :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.selenium.cucumber</groupId>
    <artifactId>CucumberReport</artifactId>
    <version>0.0.1</version>
    <dependencies>
        <!--Dependency For Cucumber Java -->
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>1.2.4</version>
        </dependency>
        <!-- Dependency For Cucumber JUnit Runner -->
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>1.2.4</version>
        </dependency>
        <!-- Dependency For Cucumber Custom Report -->
        <dependency>
            <groupId>net.masterthought</groupId>
            <artifactId>cucumber-reporting</artifactId>
            <version>2.2.0</version>
        </dependency>

    </dependencies>
    <!-- Build Part -->
    <build>
        <plugins>
            <!-- Maven Sure Fire Plug In. It is used to select runner class from any
                folder location as well as not stop to execution whenever any test fail -->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <includes>
                        <include>com/selenium/cucumber/runner/CucumberRunner.java</include>
                    </includes>
                    <printSummary>true</printSummary>
                    <testFailureIgnore>true</testFailureIgnore>
                </configuration>
            </plugin>
            <!-- This is for Cucumber Custom Report plug in we specify the configuration
                details under configuration tag. -->

            <plugin>
                <groupId>net.masterthought</groupId>
                <artifactId>maven-cucumber-reporting</artifactId>
                <version>2.0.0</version>
                <executions>
                    <execution>
                        <id>execution</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <projectName>CucumberReport</projectName>
                            <outputDirectory>${project.build.directory}/cucumber-html-reports</outputDirectory>
                            <cucumberOutput>${project.build.directory}/cucumber.json</cucumberOutput>
                            <parallelTesting>false</parallelTesting>
                            <buildNumber>1</buildNumber>
                            <checkBuildResult>false</checkBuildResult>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <!-- This is for Maven Sure Fire Report plug in.It is used to create documentation
        and html report for this project -->

    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-report-plugin</artifactId>
                <version>2.19.1</version>
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>report-only</report>
                        </reports>
                    </reportSet>
                </reportSets>
            </plugin>
        </plugins>
    </reporting>
</project> 

 





Saturday, April 9, 2016

Custom Reporting For Cucumber JVM

On our previous post we understand cucumber can generate default html report. But report is not so cool.

Here we learn how to generate a better looking and informative report. We can get this report from this github URL. This report consume the cucumber json report and create a beautiful html report.

Now we learn how to use this report in our normal Java project.

Step 1:
 We need to add all the jar files as depicted on below picture in our project.


We can download all the above mentioned jar files from Maven Repository .

Step 2:
 Now we need to write custom JUnit AfterSuite annotation because if we call the Custom Report  builder class from cucumber runner within the JUnit @After annotation then it cannot create the report, reason is cucumber report json file created after all method executed and custom report builder consume this json file and generate the report.

So we create a custom annotation class like below:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD })
public @interface AfterSuite {

}

Step 3:

We create a another class CustomCucumberRunner which will control the workflow of the Cucumber class and also the calling methods when find the expected annotation. This class extends the JUnit runner class also override the two methods

  1. getDescription()
  2. run(RunNotifier notifier)

On getDescription() method we call the cucumber getDescription() method and on run(RunNotifier notifier) method we call run method of cucumber class and then call the method which have the @After method annotation.

So code looks like below :

private void runAnnotatedMethods(Class<?> annotation) throws Exception {
if (!annotation.isAnnotation()) {
return;
}
Method[] methods = this.classValue.getMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation item : annotations) {
if (item.annotationType().equals(annotation)) {
method.invoke(null);
break;
}
}
}
}

@Override
public void run(RunNotifier notifier) {
cucumber.run(notifier);
try {
runAnnotatedMethods(AfterSuite.class);
} catch (Exception e) {
e.printStackTrace();
}
}

Step 4:
Now we call this CustomCucumberRunner class from Our original Runner class like @RunWith(CustomCucumberRunner .class). We also call the Report builder class under the @AfterSuite annotation.

So code looks like:

@RunWith(ExtendedCucumberRunner.class)
public class CucumberRunner {
@AfterSuite
public static void generateReport(){
ReportBuilder reportBuilder = new ReportBuilder(jsonFiles, configuration);
reportBuilder.generateReports();
     }
}


So our complete code looks like 

Sample feature File :

Feature: This is a sample feature file
@test1
Scenario: This is a scenario to test DataTable on Cucumber JVM.
Given scenario data
When executed from Runner Class
Then UserName and Password Like below.
| userName | password |
| TestUser1 | TestPassword1 |
| TestUser2 | TestPassword2 |

Glue Code :

public class SumFeatureTest {

@Given("^scenario data$")
public void scenarioData() throws Throwable {
System.out.println("Scenario Have Some Data");
}

@When("^executed from Runner Class$")
public void executedFromRunnerClass() throws Throwable {
System.out.println("Executed From Runner Class");
}

@Then("^UserName and Password Like below\\.$")
public void usernameAndPasswordLikeBelow(DataTable dataTable) throws Throwable {
System.out.println(dataTable.asMap(String.class, String.class));
System.out.println(dataTable.asMaps(String.class, String.class));
System.out.println(dataTable.asList(UserDetailsDTO.class).get(0).getUserName()+ "  " +dataTable.asList(UserDetailsDTO.class).get(0).getPassword());
}

}

AfterSuite annotation :

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD })
public @interface AfterSuite {


}

CustomCucumberRunner Class :

public class CustomCucumberRunner extends Runner {

private Class<?> classValue;
private Cucumber cucumber;

public CustomCucumberRunner(Class<?> classValue) throws Exception {
this.classValue = classValue;
cucumber = new Cucumber(classValue);
}

@Override
public Description getDescription() {
return cucumber.getDescription();
}

private void runAnnotatedMethods(Class<?> annotation) throws Exception {
if (!annotation.isAnnotation()) {
return;
}
Method[] methods = this.classValue.getMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation item : annotations) {
if (item.annotationType().equals(annotation)) {
method.invoke(null);
break;
}
}
}
}

@Override
public void run(RunNotifier notifier) {
cucumber.run(notifier);
try {
runAnnotatedMethods(AfterSuite.class);
} catch (Exception e) {
e.printStackTrace();
}
}

}

CucumberRunner Class :

@CucumberOptions(
plugin = { 
"html:target/cucumber-html-report",
"json:target/cucumber.json"
        },features ={"./sample.feature"},tags={"@test,@test1"},
glue ={"com/automation/steps"},strict = true,
dryRun= false,monochrome = true, snippets= SnippetType.CAMELCASE)

@RunWith(CustomCucumberRunner.class)
public class CucumberRunner {
@AfterSuite
public static void generateReport(){

/** * Report Generated Under "Custom-Report". */
File reportOutputDirectory = new File("./Custom-Report");
List<String> jsonFiles = new ArrayList<>();
jsonFiles.add("target/cucumber.json");
// jsonFiles.add("target/cucumberusage.json");

String jenkinsBasePath = "";
String buildNumber = "1";
String projectName = "cucumber-jvm";
boolean skippedFails = true;
boolean pendingFails = false;
boolean undefinedFails = true;
boolean missingFails = true;
boolean runWithJenkins = false;
boolean parallelTesting = false;

Configuration configuration = new Configuration(reportOutputDirectory, projectName);

// optional only if you need

configuration.setStatusFlags(skippedFails, pendingFails, undefinedFails, missingFails);
configuration.setParallelTesting(parallelTesting);
configuration.setJenkinsBasePath(jenkinsBasePath);
configuration.setRunWithJenkins(runWithJenkins);
configuration.setBuildNumber(buildNumber);

ReportBuilder reportBuilder = new ReportBuilder(jsonFiles, configuration);
reportBuilder.generateReports();
}

}

Friday, March 25, 2016

FreeMarker Template Engine With XML Data Model.

On previous post we learn how to marge object type data model with FreeMarker template. Now we learn how we can marge FreeMarker template with  XML data model. All steps are basically same as Object Data binding with FreeMarker except datamodel creation. Here we put the complete xml file into the map. We discuss in details in below

Step: 1
Download the latest stable Fremarker version from here.

Step: 2
Extract the downloaded folder and put the the jar in our project.

Step:3
Create Fremarker configuration object with version number and some recommend settings.
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
        cfg.setDefaultEncoding("UTF-8");
        cfg.setLocale(Locale.US);
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);


Step:4
Mentioning the template file location.

        cfg.setDirectoryForTemplateLoading(new File("./resources"));
        Template template = cfg.getTemplate("xml_Freemarker_Template.ftl");


Step: 5 
Creating Data model part.
         Map xmlData = new HashMap();
         xmlData.put("samples", freemarker.ext.dom.NodeModel.parse(new File("./resources/sampleXML.xml")));

Here we put key as "samples" basically we set the root of the xml document we can mention any name here but we need to put the same name in .ftl file also.   

Step: 6 
 Creating the output part.

  This is for console output:

        Writer consoleWriter = new OutputStreamWriter(System.out);
        template.process(xmlData, consoleWriter);


  This is for html output:     

        try (Writer fileWriter = new FileWriter(new File("output.html"))) {
            template.process(xmlData, fileWriter);
        }


Step: 7
Creating template file and save it with .ftl extention. Here it is a sample template file.

<#ftl strip_whitespace = true>

<#assign charset="UTF-8">
<#assign title="Example">
<#assign content>
This is content
</#assign>
<!DOCTYPE html>
<html>
    <head>
        <title>${title}</title>
        <meta charset="${charset}">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
    <body> 

        <!-- We need to use the same name of the root element which is set from code.
        Here it is "samples" and then follow the xml path so it is
sample.sampledata--> 
        <#foreach sample in samples.sample.sampledata>
            <div>
                id:${sample.id}
                name: ${sample.name}
                age:${sample.age}
                salary:${sample.salary}
                exp:${sample.exp}
                sex:${sample.sex}
                <#foreach address in sample.addresses>
                <div>
                    address1:${address.address1}
                    <#if "${address.address2}"!="">
                    address1:${address.address2}
                    </#if>
                </div>
                </#foreach>
            </div> 
        </#foreach>
        </body>
    </html>


Step: 8  

Sample XML File.

<sample>
    <sampledata>
        <id>1</id>
        <name>TestName1</name>
        <age>28</age>
        <salary>50000</salary>
        <exp>6.6</exp>
        <sex>m</sex>
        <addresses>
            <address1>My Address1</address1>
            <address2></address2>
        </addresses>
    </sampledata>


 Complete code given below :

Main Class :

package freemarker;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;

public class FreemarkerXML {

    public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException, TemplateException {
        /**
         * Create Fremarker configuration object with version number and some
         * recommend settings.
         */

        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
        cfg.setDefaultEncoding("UTF-8");
        cfg.setLocale(Locale.US);
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

        /**
         * Mentioning the template file location.
         */

        cfg.setDirectoryForTemplateLoading(new File("./resources"));
        Template template = cfg.getTemplate("xml_Freemarker_Template.ftl");
        Map xmlData = new HashMap();
      
        /**
         * Read the XML file and process the template using FreeMarker.
         */

        xmlData.put("samples", freemarker.ext.dom.NodeModel.parse(new File("./resources/sampleXML.xml")));

        /**
         * Creating the output part.
         */

        Writer consoleWriter = new OutputStreamWriter(System.out);
        template.process(xmlData, consoleWriter);

        /**
         * For the sake of example, also write output into a file.
         */

        try (Writer fileWriter = new FileWriter(new File("output.html"))) {
            template.process(xmlData, fileWriter);
        }
    }
}


Sample XML:

<sample>
    <sampledata>
        <id>1</id>
        <name>TestName1</name>
        <age>28</age>
        <salary>50000</salary>
        <exp>6.6</exp>
        <sex>m</sex>
        <addresses>
            <address1>My Address1</address1>
            <address2></address2>
        </addresses>
    </sampledata>
    <sampledata>
        <id>2</id>
        <name>TestName2</name>
        <age>26</age>
        <salary>60000</salary>
        <exp>5</exp>
        <sex>f</sex>
        <addresses>
            <address1>My Address1</address1>
            <address2>My Address2</address2>
        </addresses>
    </sampledata>
</sample>

 

Temmplate File :

<#ftl strip_whitespace = true>

<#assign charset="UTF-8">
<#assign title="Example">
<#assign content>
This is content
</#assign>
<!DOCTYPE html>
<html>
    <head>
        <title>${title}</title>
        <meta charset="${charset}">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
    <body>  
        <#foreach sample in samples.sample.sampledata>
            <div>
                id:${sample.id}
                name: ${sample.name}
                age:${sample.age}
                salary:${sample.salary}
                exp:${sample.exp}
                sex:${sample.sex}
                <#foreach address in sample.addresses>
                <div>
                    address1:${address.address1}
                    <#if "${address.address2}"!="">
                    address1:${address.address2}
                    </#if>
                </div>
                </#foreach>
            </div> 
        </#foreach>
        </body>
    </html>

Thursday, March 24, 2016

Free Marker Template Engine With Object Data Model.

Template Engine is used when we separate our HTML coding from our source code. It is basically used when we try to follow MVC architecture in our code. Lots of  template engine available on market like Velocity,Freemarker,Rythm etc.

Here we try to focus on Freemarker template engine.

Template engines generates output with the help of template file and run time data.

 Template marker can use 2 different type of data model
  1. Object in Map model 
  2.  XML data in Map model
We need to follow the below steps to play with Freemarker template:

Step: 1
Download the latest stable Fremarker version from here.

Step: 2
Extract the downloaded folder and put the the jar in our project.

Step:3
Create Fremarker configuration object with version number and some recommend settings.
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
        cfg.setDefaultEncoding("UTF-8");
        cfg.setLocale(Locale.US);
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);


Step:4
Mentioning the template file location.
        cfg.setDirectoryForTemplateLoading(new File("./resources"));
        Template template = cfg.getTemplate("freemarker_template.ftl");


Step: 5 
Creating Data model part.

        List<SampleDataDTO> freemarkerListObjectData = new ArrayList<>();
        freemarkerListObjectData.add(new SampleDataDTO(1, "Test1", 28.2f, new BigDecimal("40000.00"), 5.2, 'm'));
        freemarkerListObjectData.add(new SampleDataDTO(2, "Test2", 29.2f, new BigDecimal("50000.00"), 6.2, 'f'));
       
        List<String> freemarkerListStringData = new ArrayList<>();
        freemarkerListStringData.add("1");
        freemarkerListStringData.add("2");
       
        Map<String, Object> freemarkerMapData = new HashMap();
        freemarkerMapData.put("mapdata1", "My First FreeMarker Page");
        freemarkerMapData.put("mapdata2", "UTF-8");
        freemarkerMapData.put("SampleDataDTO", freemarkerListObjectData);
        freemarkerMapData.put("SampleList", freemarkerListStringData);


Step: 6 
 Creating the output part.

  This is for console output:
        Writer consoleWriter = new OutputStreamWriter(System.out);
        template.process(freemarkerMapData, consoleWriter);


  This is for html output:     

        try (Writer fileWriter = new FileWriter(new File("output.html"))) {
            template.process(freemarkerMapData, fileWriter);
        }


Step: 7
Creating template file and save it with .ftl extention. Here it is a sample template file.


<#ftl strip_whitespace = true>

<#macro another_macro>
<#list SampleList as List>
    <li>${List_index + 1}. ${List}</li>
</#list>
</#macro>   


Complete code given below :

Main Class :

package freemarker;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class FreeMarker {

    public static void main(String[] args) throws TemplateException, IOException {
         /**
         * Create Fremarker configuration object with version number and some recommend settings.
         */

        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
        cfg.setDefaultEncoding("UTF-8");
        cfg.setLocale(Locale.US);
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);


         
          /**
         * Mentioning the template file location.
         */

        cfg.setDirectoryForTemplateLoading(new File("./resources"));
        Template template = cfg.getTemplate("freemarker_template.ftl");
        

        /**
         * Creating Data model part.
         */

        List<SampleDataDTO> freemarkerListObjectData = new ArrayList<>();
        freemarkerListObjectData.add(new SampleDataDTO(1, "Test1", 28.2f, new BigDecimal("40000.00"), 5.2, 'm'));
        freemarkerListObjectData.add(new SampleDataDTO(2, "Test2", 29.2f, new BigDecimal("50000.00"), 6.2, 'f'));

       
        List<String> freemarkerListStringData = new ArrayList<>();
        freemarkerListStringData.add("1");
        freemarkerListStringData.add("2");
       
        Map<String, Object> freemarkerMapData = new HashMap();
        freemarkerMapData.put("mapdata1", "My First FreeMarker Page");
        freemarkerMapData.put("mapdata2", "UTF-8");
        freemarkerMapData.put("SampleDataDTO", freemarkerListObjectData);
        freemarkerMapData.put("SampleList", freemarkerListStringData);
        

        /**
         * Creating the output part.
         */

        Writer consoleWriter = new OutputStreamWriter(System.out);
        template.process(freemarkerMapData, consoleWriter);

       
        /**
         * For the sake of example, also write output into a file.
         */

        try (Writer fileWriter = new FileWriter(new File("output.html"))) {
            template.process(freemarkerMapData, fileWriter);
        }
    }
}


DTO or Bean Class 

package freemarker;

import java.math.BigDecimal;


public class SampleDataDTO {
    private int id ;
    private String name;
    private float age;
    private BigDecimal salary;
    private double exp;
    private char sex;

    public SampleDataDTO() {
    }
   
    public SampleDataDTO(int id, String name, float age, BigDecimal salary, double exp, char sex) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.exp = exp;
        this.sex = sex;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public float getAge() {
        return age;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public double getExp() {
        return exp;
    }

    public char getSex() {
        return sex;
    }


.ftl files :

 Main Ftl File:

<#ftl strip_whitespace = true>
<#-- Main template file starts-->
<#-- Importing Other template file-->

<#import "sub_freemarker_template.ftl" as sub_template>
<#import "another_freemarker_template.ftl" as another_template>
<#-- Creating variable.-->
<#assign myVar>This is my variable</#assign>
<!DOCTYPE html>
<html>
    <#-- Calling Macro-->
    <@sub_template.head>
     </@sub_template.head>
    <body>
        <#-- Checking if condition and print variable.-->
        <#if myVar == "This is my variable">
            <div>${myVar}</div>
        <#elseif myVar == "">
            <div>blank</div>
        <#else>
            <div>blank</div>
        </#if>
        <#-- Using Macro.-->
        <@another_template.another_macro> 
        <#-- Showing data from  datamodel. Here it is simple key from Map-->  
        <div>${mapdata1}</div>
        <div>${mapdata2}</div>
        </@another_template.another_macro> 
            <ul>

        <#-- Showing data from  datamodel. Here it is List Object from Map-->   
                 <#list SampleDataDTO as Sample>
                    <li>${Sample_index + 1}. ${Sample.name} from ${Sample.age}</li>
                </#list>
            </ul>
        </body>
    </html>


Macro Ftl
<#ftl strip_whitespace = true>
<#macro head>
<#assign charset="UTF-8">
<#assign title="Example">
<#assign content>This is content</#assign>
    <head>
        <title>${title}</title>
        <meta charset="${charset}">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
</#macro>


Macro Ftl
 
<#ftl strip_whitespace = true>

<#macro another_macro>
<#list SampleList as List>
    <li>${List_index + 1}. ${List}</li>
</#list>
</#macro>