Monday, December 17, 2012

Spring Controller class for jQuery grid

I have already posted an article of creating jQuery grid using struts 2 action class. This post explains, how to write spring controller class which supports to implement a jQuery grid. If you want to get through of crating a jQuery grid with spring controller class, you must first read my previous post which has shown bellow.

How to use jQuery grid with struts 2 without plugin ?

I am not going to repeat the same stuff which I have explained in my previous post here, because this post has only a few slight differences from my previous post. The previous post used a struts 2 action class as controller and this post uses a spring controller class instead of that. And also, previous post uses Province model class directly to represent the data. In this post, I am using a ProvinceDTO (Data Transfer Object) to bring data into controller level. Except above, the whole technique of implementing grid is exactly similar to previous post. So I am 100% sure that, if you go through the previous post first, you will definitely implement the same grid with spring controller class.

When creating jQuery grid with JSON response data, we need to properly synchronize some fields with client side to work grid properly. Those fields include,

  • rows       -The number of rows for one page.
  • page       -The page number requested.
  • sord        -Sorting order.
  • sidx         -Sorting index.
  • total        -Total number of records.
  • records   -The number of records returned for particular search criteria.
  • _search   -Either 'true' or 'false' indicating a particular request is search or not.
  • oper        -The type of operation, either 'add', 'del' or 'edit'

There are a few more fields like above which I have not mentioned here. You can refer jQuery documentation to know those. 

In my previous post, with struts 2 action class, I have placed these fields with in the action class itself. With spring controller class, I created separate generic Data Transfer Object to keep these properties and also 'gridModel' which is a collection of any type holding grid data. Every Data Transfer Object which carries data to create jQuery grid must be inherited from this grid support Data Transfer Object. 

The code for grid support Data Transfer Object is as follows.

 package com.blimp.dto;

 import java.util.List;

 public class GridSupportDTO<T> { 

 private List<T> gridModel = null;
 
 private Integer rows = 0;
 private Integer page = 0;
 private String sord;
 private String sidx;
 private Integer total = 0;
 private Integer records = 0;
 private Boolean _search;
 private String oper = null;
 
 public List<T> getGridModel() {
      return gridModel;
 }
 public void setGridModel(List<T> gridModel) {
      this.gridModel = gridModel;
 }
 public Integer getRows() {
      return rows;
 }
 public void setRows(Integer rows) {
      this.rows = rows;
 }
 public Integer getPage() {
      return page;
 }
 public void setPage(Integer page) {
      this.page = page;
 }
 public String getSord() {
      return sord;
 }
 public void setSord(String sord) {
  this.sord = sord;
 }
 public String getSidx() {
      return sidx;
 }
 public void setSidx(String sidx) {
      this.sidx = sidx;
 }
 public Integer getTotal() {
      return total;
 }
 public void setTotal(Integer total) {
      this.total = total;
 }
 public Integer getRecords() {
      return records;
 }
 public void setRecords(Integer records) {
      this.records = records;
 }
 public Boolean get_search() {
      return _search;
 }
 public void set_search(Boolean _search) {
      this._search = _search;
 }
 public String getOper() {
      return oper;
 }
 public void setOper(String oper) {
      this.oper = oper;
 } 
}

In above class, 'gridModel' is a generic collection which carries data for the grid. Let's see my ProvinceDTO.java class which represent data for each a single grid row. 

 package com.blimp.dto;

 import java.io.Serializable;

 public class ProvinceDTO extends GridSupportDTO<ProvinceDTO> implements Serializable {

 private static final long serialVersionUID = 8805507589278247940L;
 
 private Long id;
 
 private String name;
 
 private String provinceStatus;

 public Long getId() {
      return id;
 }

 public void setId(Long id) {
      this.id = id;
 }

 public String getName() {
      return name;
 }

 public void setName(String name) {
      this.name = name;
 }

 public String getProvinceStatus() {
      return provinceStatus;
 }

 public void setProvinceStatus(String provinceStatus) {
      this.provinceStatus = provinceStatus;
 }
}

I am going to implement the same grid with same fields as my previous post. In my previous post, I have used Provice.java entity class, but here, I am using ProvinceDTO.java class, since I don't want to clutter my entity classes with grid specific attributes or don't want to extends from a class which has grid specific attributes. Other thing is, I am using Data Transfer Object pattern with this project.

Next, we'll see our spring controller class which supports to implement jQuery grid.

package com.blimp.webapp.controller;

import java.security.Principal;
import java.util.List;
import java.util.Locale;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.blimp.dto.Page;
import com.blimp.dto.ProvinceDTO;
import com.blimp.model.Province;

@Controller
public class ProvinceController {

    @Autowired
    private ProvinceService provinceService;
 
    @RequestMapping(value = "*search*", method = RequestMethod.GET)
    public @ResponseBody
    ProvinceDTO searchProvince(Principal principal, ProvinceDTO provinceDTO, 
    Model model, Locale locale) {
  
    try {
   
         Page<Province, ProvinceDTO> requestedPage = new Page<Province, ProvinceDTO>(); 
         requestedPage.setPage(provinceDTO.getPage());
         requestedPage.setRows(10);
   
         Page<Province, ProvinceDTO> resultPage = provinceService.findByCriteria(provinceDTO, requestedPage);
         List<ProvinceDTO> provinceList = resultPage.getResultDtoList();
   
         provinceDTO.setGridModel(provinceList);
         provinceDTO.setRecords(resultPage.getRecords());
         provinceDTO.setTotal(resultPage.getTotals());
   
    } catch (Exception e) {
         e.printStackTrace();
    }
  
    return ProvinceDTO;
  }
}

Note that, I have passed 'provinceDTO' as a parameter for above controller method and also the method returns the same 'provinceDTO' and ultimately as a JSON response to the browser. I have annotated the method with '@ResponseBody' annotation which converts what ever the returning stuff from this method into JSON. I have updated 'gridModel', 'records' and 'totals' with appropriate data. The methods of service layer, DAO layer are exactly similar as in my previous post. Don't be lazy, have a look on my previous post, If you really need to implement jQuery grid with spring controller class. 

From the client side Javascript code, you need only to change the 'url' and 'editurl' attributes of the grid as follows.

url:CONTEXT_ROOT + '/search'
editurl:CONTEXT_ROOT + '/edit'

I haven't implement the controller method for edit function in above controller class. That's it for this post. I hope this will be a good start working with jQuery grid and spring controller. If you have problem of achieving this, feel free to contact me with a simple mail. 

Sunday, November 18, 2012

How to read a dynamically growing file with Java?

This small post explains, how to read a text file which grows dynamically, using Java. The catalina.out file which displays tomcat's console is a good example for a file which grows dynamically. 

The following example shows, how to read tomcat's catalina.out file with Java. This file grows when the applications deployed on tomcat are being accessed by users. The following program displays newly adding contents of catalina.out file just one second delay and keep continuing it. 

The Java's 'RandomAccessFile' class provides great facilities to read this kind of files.  The major usage of it for the following program is, it provides a feature so that we can read a file from a particular point to onwards. Let's see our small program. 

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Timer;
import java.util.TimerTask;


public class ConsoleReader {

   /**
    * @param args
    */
    public static void main(String[] args) {
    File file = new File("/home/semika/apache-tomcat-7.0.25/logs/catalina.out");
       try {
           RandomAccessFile r = new RandomAccessFile(file, "r");
           //First time read
           String str = null;
           while((str = r.readLine()) != null) {
            System.out.println(str);
           }
           r.seek(r.getFilePointer());
           startTimer(r);
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       } 
    }
 
    private static void startTimer(final RandomAccessFile r) {
     Timer timer = new Timer();
     timer.scheduleAtFixedRate(new TimerTask() {
     @Override
     public void run() {
     String str = null;
     try {
          while((str = r.readLine()) != null) {
           System.out.println(str);
          } 
          r.seek(r.getFilePointer()); 
     } catch (IOException e) { 
         e.printStackTrace();
     }
      }
 }, 0, 1000);
    }
}

In the above program, it reads catalina.out file line by line and displays it in console. When you run the program, it will read the whole file up to the end at first and then immediately start a timer to read dynamically adding contents to the file. The timer will always start to read file from a particular point, in above case, it will always start to read the file from the next line which last line was displayed.

While you are accessing your applications deployed in your tomcat container, just keep looking at the output of above program. You can see the tomcat's console just like, you have used to see it before.

Monday, November 12, 2012

Java enums are more than constant fields.

An enum is a type which defines set of fixed constants. For example, if you think generally, the seasons of the year, the planets in the solar system can be defined as enums. Also, you can identify constant types  in your application and defined those as enums which gives you much reliability, maintainability and also the flexibility. Enums were introduced to the Java language with 1.5 release which provides great functionality than what we know, and what we are using. Before introducing enums to the language, we used int, string constants which have many shortages. 

With this post, I am going to discuss the advantages of using enums and the shortcomings of using traditional constants approach. Suppose, we want to keep constant types for seasons of the year. Before enum was introduced, we normally defined a constant group as follows. 

 public static final int SEASON_SPRING = 3;
 public static final int SEASON_SUMMER = 6;
 public static final int SEASON_AUTUMN = 9;
 public static final int SEASON_WINTER = 12;
The value assigned for each field is, the starting month of the season. For example, the summer season will start from 6th month of the year. In real world, the starting month of each season will vary from country to country. I used above values just for illustration purpose. 

Let's discuss the shortcomings of the above approach when comparing with enum types. 

Constant fields don't provide type safety. Suppose you have written a method which returns the months belong to each season.

private List<String> getMonthsOfSeason(int seasonStartingMonth) {
  
    switch(seasonStartingMonth) {
       case 3:
           return CalendarUtil.getMonthsOfYear(new int[]{2,3,4});
           //[March, April, May]
       case 6:
           return CalendarUtil.getMonthsOfYear(new int[]{5,6,7});
           //[June, July, August]
       case 9:
           return CalendarUtil.getMonthsOfYear(new int[]{8,9,10});
           //[September, October, November]
       case 12:
           return CalendarUtil.getMonthsOfYear(new int[]{11,0,1});
           //[December, January, February]
       default:
           return new ArrayList<String>(); 
    }
}

If we use traditional int constants approach, we have to implement similar method like above to get the months of a season. I will show you the code for CalendarUtil class later in this post.

The above method accepts any integer value as the season starting month. By mistakenly, you may pass a different integer constant field (some number except 3,6,9,12) into the method instead of passing one of the season number which has been defined as constants above. In that kind of scenario, the method will returns empty list which may result unexpected output.

Though, You can add considerable validation to the above method, still that method is brittle. The other thing is, you may compare the parameter value with any integer since defined constants are integer type.

   
   If(seasonStartingMonth == QUATER_THREE) {
     //some code
   }

In the above code 'QUATER_THREE' is some different integer constant field defined in the same class for different purpose. Our programmer has mistakenly compared 'seasonStartingMonth' variable with QUATER_THREE constant field. But this is valid comparison, no compile error, but it will give wrong output. Programmer actually intended to compare 'seasonStartingMonth' variable with one of the our defined season constant field. 

Java does not provide namespaces to distinguish each constant groups. We have to use prefixes when two int constant groups have identically named fields. I have used SEASON_ for season group for that purpose. 

The int constants are compile-time constants. The int enums are compiled into the clients that use them. If the int associated with an enum constant is changed, its clients must be recompiled. If they aren’t, they will still run, but their behaviour will be undefined. 

The int constants don't give good debugging information. The int constant fields do not give helpful information when you are debugging your code. If you print some int constant field, it will print just a number which does not make sense. There is no easy way to translate int constant fields into printable strings. 

There is no reliable way to iterate over all the constants defined in a particular constant group and also the size of the constant group. 

Above, I have highlighted some of the shortcoming when using traditional constant field approach.

I have small utility class as follows.

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;


public final class CalendarUtil {

    //Returns list of names of the months for a given array of month numbers.
    public static List<String> getMonthsOfYear(int[] months) {
         List<String> monthArray = new ArrayList<String>(3);
         Calendar cal = Calendar.getInstance();

         for (int i = 0; i < months.length; i++) {
             cal.set(Calendar.MONTH, months[i]);
             monthArray.add(cal.getDisplayName(Calendar.MONTH,
                    Calendar.LONG, Locale.ENGLISH));
         }
         return monthArray;
    }
}

In the above utility class, the 'getMonthsOfYear()' method returns list of month's name for a given array of month numbers of the year.

Now,let's try to do the same thing by using enum. The following code shows an enum definition for seasons of the year.

import java.util.ArrayList;
import java.util.List;

public enum Season {

   SEASON_SPRING(3), //[March, April, May]
   SEASON_SUMMER(6), //[June, July, August]
   SEASON_AUTUMN(9), //[December, January, February]
   SEASON_WINTER(12);//[September, October, November]
 
   private final int startingMonth;
 
   SEASON(int startingMonth) {
       this.startingMonth = startingMonth;
   }
 
   public List<String> getMonthsOfSeason() {

       switch(startingMonth) {
           case 3:
             return CalendarUtil.getMonthsOfYear(new int[]{2,3,4});
           case 6:
             return CalendarUtil.getMonthsOfYear(new int[]{5,6,7});
           case 9:
             return CalendarUtil.getMonthsOfYear(new int[]{8,9,10});
           case 12:
             return CalendarUtil.getMonthsOfYear(new int[]{11,0,1});
           default:
             return new ArrayList$lt;String>(); 
       }
    }
}

See how Java enums are smart? Java enums are classes that export one instance for each enumeration constant via a public static final field. This kind of enum declaration provides compile time safety. For example, consider the following method.

private List<String> getMonthsOfSeason(Season season) {

}

This method guaranteed that it won't accept any other types of values except Season. If we try to pass some other types, compiler will point it out as an error. 

Since Java enum has it's own namespace, identically named constants can coexist with out any problem. For example, We can use same enum constant field name with another enum declaration. 

We can easily convert enum constant fields to printable string using toString() method.

Java enum allows us to keep methods and fields associated with it. In the above example, I have declared one field and method with the enum. I have defined the method with the enum it self which returns months of the season. This is very reliable and convenience implementation. The likelihood of happening bugs in the application is comparatively less. If we want to get the months of winter seasion, we can do it as follows.

List<String> months = Season.SEASON_WINTER.getMonthsOfSeason();

In the above example, the defined method behaves similarly for every constant field. But, We can implement method which behaves differently for each constant field on the enum. I have discussed about field specific method implementation with another post. Please have a look on following URL.

http://skillshared.blogspot.com/2012/01/java-enum-constant-specific-method.html

We can associate data with each constant field of a enum. I have specified starting month number with each constant field of Season enum. We can associate data as many with the enum constant field with the declared fields with corresponding types of data and also the constructor parallel to those data. Enums are immutable, so all fields should be final.

Also, if you want to iterate over all the constant fields of the enum, you can do it as follows.

for (Season s : Season.values()) {
   System.out.println("Months of seasion : " + s.toString());
   System.out.println(s.getMonthsOfSeason());
}

I am going to conclude this article with nice example. Consider the eight planets of our solar system. There are tow constant attribute for each planet has, a mass and a radius, and from these two attributes you can compute its surface gravity. This in turn lets you compute the weight of an object on the planet’s surface, given the mass of the object. Here’s how this enum looks.The numbers in parentheses after each enum constant are parameters that are passed to its constructor. In this case, they are the planet’s mass and radius.
public enum Planet {
    MERCURY(3.302e+23,2.439e6),
    VENUS (4.869e+24,6.052e6),
    EARTH (5.975e+24,6.378e6),
    MARS(6.419e+23,3.393e6),
    JUPITER(1.899e+27,7.149e7),
    SATURN (5.685e+26,6.027e7),
    URANUS (8.683e+25,2.556e7),
    NEPTUNE(1.024e+26,2.477e7);

    private final double mass;
    private final double radius;
    private final double surfaceGravity;

    private static final double G = 6.67300E-11;

    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
        surfaceGravity = G * mass / (radius * radius);
   }

   public double mass() {
         return mass;
   }

   public double radius() {
         return radius;
   }

   public double surfaceGravity() {
         return surfaceGravity;
   }

   public double surfaceWeight(double mass) {
        return mass * surfaceGravity;
   }
}

Sunday, November 11, 2012

How to display months of the year with Java?

The Calendar is very useful utility class provided by Java for manipulating calender fields. It represents months of the year in integer format starting from 0 and ending with 11, which 0 represents 'January' and 11 represents 'December'. The 'getDisplayName' method of Calendar class returns the string representation of the calendar field value in the given style and locale. If no string representation is applicable, null is returned. 

The following Java program displays months of the year in two formats.

import java.util.Calendar;
import java.util.Locale;

public class CalendarUtil {

    public static void main(String[] args) {
        displayMonthsInLongFormat();
        displayMonthsInShortFormat();
    }
 
    private static void displayMonthsInLongFormat() {
  
        Calendar cal = Calendar.getInstance();
  
        System.out.println("Months of the year in long format");
        System.out.println("---------------------------------");
  
        for (int i = 0; i < 12; i++) {
            cal.set(Calendar.MONTH, i);
   
             System.out.println(cal.getDisplayName(Calendar.MONTH, 
                           Calendar.LONG, Locale.ENGLISH));; 
        }
     }
 
     private static void displayMonthsInShortFormat() {
  
         Calendar cal = Calendar.getInstance();
  
         System.out.println("Months of the year in short format");
         System.out.println("---------------------------------");
  
         for (int i = 0; i < 12; i++) {
             cal.set(Calendar.MONTH, i);
   
             System.out.println(cal.getDisplayName(Calendar.MONTH, 
                       Calendar.SHORT, Locale.ENGLISH));; 
         }
     } 
}
The above program will generate the following output.

Months of the year in long format ---------------------------------
January
February
March
April
May
June
July
August
September
October
November
December

Months of the year in short format ---------------------------------
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec

Saturday, November 10, 2012

How to run Ant targets with Maven?

The maven-antrun-plugin allows us to run ant targets with various maven build phases. I am going to explain very practical usage of maven-antrun-plugin specially for developers with development environment.

Normally with maven build, you will bundle your project either to a war file or ear file. You can directly copy this war or ear file into the server deployment folder by using maven-antrun-plugin. If your sever is tomcat, you can directly copy your archive file to 'webapps' folder easily.  Some developers are used to copy the archive file to the server deployment folder manually even with their development. For them, this post will be very helpful.

If you want to use maven-antrun-plugin to copy your archive file into the server deployment folder every time when you build the project, you can add the following plugin into your pom.xml file and use what ever the ant targets as you wish.

Which pom.xml file, I am going to put this plugin to?

That is a good question. If you have multi module project, you should probably have either ear module or war module. Select the pom.xml file of that module and place the following plugin there. When you buld that project module, most of the time, it will be the last module bulding when you build your project in root level, maven will create either war file or ear file inside the target directory of your project module. We can configure maven-antrun-plugin so that it will copy that war file or ear file into server deployment folder.

In my case, I have multi module project and one module is a web module. I should place the the maven-antrun-plugin into web module's pom.xml file.

<build>
    <finalName>shims-web</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.7</version>
            <executions>
                <execution>
                    <phase>install</phase>
                    <configuration>
                          <target>
                              <copy file="${project.build.directory}/shims-web.war" todir="${env.CATALINA_HOME}/webapps"/>
                          </target>
                    </configuration>
                    <goals>
                         <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

If you inspect the above snip of pom.xml file, I have given 'install' as the phase of execution. It simply says that "execute this ant target just after the install life cycle phase" of the maven build. With the execution of 'install' , maven will the package the whole project into the local repository as a war file or ear file, for use as a dependency in other projects locally. Also this will create the same file in the target directory of your workspace as well.

Our target is to copy that file from target director to server deployment folder with the build. The  ${env.CATALINA_HOME} will refer to the our tomcat installation directory.

Now build the your project or project module. You can navigate to your project directory or project module directory where our modified pom.xml is. Run the following command.

$mvn clean install

The above command will traverse into all of the sub projects and run clean, then install (including all of the prior steps). You can run that command in root project level which build the entire project, or you can run it for specific project module, which build only that project.


.......................
[INFO] --- maven-antrun-plugin:1.7:run (default) @ shims-web ---
[INFO] Executing tasks
main:
         [copy] Copying 1 file to /home/vinesh/apache-tomcat-7.0.25/webapps
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS ........................

If you are using m2eclipse to build your project, sometime, you may encounter strange scenario as follows. We hope maven will copy our archive file to the deployment folder of the server with the build completion. But sometime, maven will copy our archive file into the project workspace folder itself by creating the directories structure which we have mentioned as the destination directory in our pom.xml file of our ant copy target.

For example, in this case, it will create following directory structure in our local workspace folder rather than copying the file into the server folder. Just have a look on following image.

We are not expecting this scenario.

How can we overcome this?

You have to edit maven build target in eclipse by specifying the CATALINA_HOME environment variable. 

You can do this as follows.

Open "Run Configurations" window in eclipse and expand "Maven Build" category. You can see all the maven targets, you have created so far, have been listed there. Select the maven target which build your project with "clean install" goal.

And then open "Environment" tab. There you can add new variable for the CATALINA_HOME environmental variable as follows.

Click on 'Apply' button and then run your maven target again. Look into your eclipse console carefully. You can see that maven is copying your archive file into server deployment folder.

You may also like:

Thursday, November 8, 2012

How to list down all maven available properties?

It will be very important for you to know all the available maven properties when you are working with maven project. We know few properties. Some of those properties are ${project.groupId}, ${project.version} and few more. I intended to give you a technique to find out all the available properties given by maven which will be very helpful for you. 

Here is the way. You can create a new simple maven project. It may be either jar project or war project. Even you can use one of your existing project too. Open the project pom.xml file and see the following section is in your pom.xml file.

<build>
    <plugins>
        <plugin>
             ..............
        </plugin>
   </plugins>
</build>

Since, We are going to use maven-antrun-plugin, we need to add this plugin into our project pom.xml file. If the above section is not in our pom.xml file, you have to add above section into your pom.xml file. Next copy and past the following plugin inside <plugins> section.
<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.7</version>
    <executions>
         <execution>
              <phase>install</phase>
              <configuration>
                    <target>
                        <echoproperties />
                    </target>
              </configuration>
              <goals>
                  <goal>run</goal>
              </goals>
         </execution>
    </executions>
</plugin>

The maven-antrun-plugin allows us to run ant targets with maven. The above ant target will execute when we run 'clean install' maven goal. Notice that I have specified 'install' as the phase which means that this ant target will execute with the maven 'install' goal runs. Navigate to your project directory using command line.

$cd <path to your project directory>
$mvn clean install

It will start the building the project and meanwhile, lists down all the maven properties. You will get similar output as follows.
[echoproperties] #Ant properties
[echoproperties] #Thu Nov 08 19:57:30 IST 2012
[echoproperties] ant.core.lib=/home/vinesh/.m2/repository/org/apache/ant/ant/1.8.2/ant-1.8.2.jar
[echoproperties] ant.file=/home/vinesh/workspace-maaven/shims/shims-web/pom.xml
[echoproperties] ant.file.maven-antrun-=/home/vinesh/workspace-maaven/shims/shims-web/target/antrun/build-main.xml
[echoproperties] ant.file.type.maven-antrun-=file
[echoproperties] ant.java.version=1.6
[echoproperties] ant.project.default-target=main
         ....................
         ....................
If you don't know how to create maven projects, please look the following URL. 

How to create multi module project with maven.

 
You may also like:

Wednesday, November 7, 2012

Why @Override annotation is important in Java?

The 'Override' annotation was introduced with Java 1.5 release which indicates that the annotated method declaration overrides the declaration in a super class. This annotation is used highly and most of the modern IDE's added this annotation automatically when we use some IDE's features. If you consistently use this annotation, it will protect you from a large class of sever bugs. Let's have some example. Consider the following class declaration.

public class Employee {

    private String nicNo;
    private String name;
 
    public Employee(String nicNo, String name) {
         super();
         this.nicNo = nicNo;
         this.name = name;
    }
    public String getNicNo() {
         return nicNo;
    }
    public void setNicNo(String nicNo) {
         this.nicNo = nicNo;
    }
    public String getName() {
         return name;
    }
    public void setName(String name) {
         this.name = name;
    }
    public int hashCode() {
         final int prime = 31;
         int result = 1;
         result = prime * result + ((nicNo == null) ? 0 : nicNo.hashCode());
         return result;
    }
    public boolean equals(Employee obj) {
         return obj.nicNo == this.nicNo;
    }
}

Sunday, November 4, 2012

Reduce the scope of local variable in Java

Old programing languages such as C,C++ force us to declare local variables at the head of the code block. Some programers used to continue the same with Java. But it is not a good practice though Java lets us declare local variable anywhere in the code.

The most easy way to reduce the scope of local variable is, declare local variables where it is first used. This will reduce the scope of local variables and also this will increase the code readability. If you declare your local variable at top of code block or method, by the time that variable is used, the code reader might not remember about the variable.

With this small post, I am going to give you an example which creates a bug in the application which is very hard to detect. We know that "Loops" have a special opportunity to minimize the scope of variables. The traditional for loop and new for-each loop allows you to declare loop variables limiting their scopes to the region where they are needed. 

Saturday, November 3, 2012

How to create multi module project with maven?

It is more than three years, I have started to use maven and it is a great tool, I have ever used. The support of repository management and the features available to create multi module project are excellent. With this tutorial, I am going to show you, how to create a multi module project with maven. I am using eclipse as IDE and 'm2eclipse' plugin for eclipse.

The following picture shows the project structure in eclipse.

My project is 'shims' and it has two modules as 'shims-core' and 'shims-web'. One module contains all business logic classes and data access objects which is bundled as jar file. And the other module is the web module which contains controller related classes (Struts2 action or Servlets or Spring controller classes) and view related stuff.

The tutorial summary is as follows.
Installing maven

Download the Apache maven distribution and extract it some where in your computer. You can download the apache maven from here. I have extracted the distribution into the following location in my computer.

/home/vinesh/apache-maven-3.0.4

Next, You have to set 'MAVEN_HOME' environmental variable and add the path of maven's bin folder to classpath. In Ubuntu, you can set the variable as follows. You have to add the following two lines into bashrc file. First, run the following command to open 'bashrc' file.

$ vi ~/.bashrc

And add the following two lines.

export MAVEN_HOME=/home/vinesh/apache-maven-3.0.4
export PATH=$MAVEN_HOME/bin:$PATH

In windows, setting an environment variable is not that much difficult. So I am not going to explain that in this tutorial.

After editing 'bashrc' file, just open a command window and run the following command.

$ mvn -version

If you get the output similar to following, you are successfully configured apache maven.

Apache Maven 3.0.4 (r1232337; 2012-01-17 14:14:56+0530)
Maven home: /home/semika/apache-maven-3.0.4
Java version: 1.6.0_30, vendor: Sun Microsystems Inc.
Java home: /home/semika/java/jdk1.6.0_30/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "2.6.38-16-generic", arch: "amd64", family: "unix"

You are done with the first step and let's move to next step.  

Configure m2eclipse, maven with eclipse 

You have to install m2eclipse plugin first. You can install m2eclipse plugin via eclipse it self. I am using eclipse 'Indigo' version and there may be slight differences with other version of eclipse. 

This is the update URL for m2eclipse.

http://download.eclipse.org/technology/m2e/releases

Go to Help --> Install New Software. You will get the following window.



You can add the above update URL and continue the wizard. Normally this will take some time and after finishing the installation, you have to restart the eclipse. Go to Window -->Preferences. You should see that 'Maven' category has added to preferences categories.


We are not going to use embedded maven coming with the plugin as I have highlighted in the above image. Instead, we are going to configure our maven installation with eclipse. 

Click the 'Add' button and locate your maven installation directory. For further clarification see the image bellow. 


You can see now, the plugin has been configured with our maven installation. Now, you are ready to create projects using m2eclipse. Let's start to create our multi module project.

Eclipse provides great facilities to create multi module projects and relationships among the modules. In this tutorial, I am going to create a maven multi module projects from the scratch.




 
Creating maven parent project

Step01: Go to File --> New --> Project.

You will get 'New Project' window. Under 'Maven' category, select 'Maven Project'. Click 'Next' button. You will get 'New Maven Project' window. See the bellow image.



Step 02: We are going to create simple maven project. As I have pointed out from right side image, make sure to check 'Create a simple project' checkbox. Click on 'Next' button. Then you will get the following window.

I feel, it is better to give little explanation about the four fields highlighted with red color box in the right side image.

Group Id: This is the root packaging structure of your project. Make sure to use the same root package structure for every module in the project. 

Artifact Id : This is similar to your project module name. In right side image, I have given 'shims' as the artifact id. That is the parent project of my all project modules.

Packaging: This is very important when creating multi module project with maven. There are several options which you can select as the packaging. If you want to create a parent project, we are doing like now, you have to select 'pom' as the packaging. To create Java module, select 'jar' and for web module, select 'war'. I will explain later on this tutorial, how to create a 'jar' and 'war' module. You know that 'shims-core' is a jar module which contains all the business logic and data access objects and 'shims-web' is a web module containing all controller  and view related stuff.

Click the 'Finish' button and see, maven is creating the parent project for our multi module project. Let's little examine this.

Open the 'pom.xml' file. It should be similar to the following.


<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.semika</groupId>
  <artifactId>shims</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>
</project>


Let's create our first child module.  

Creating maven child jar module

We want to keep our all business logic and DAO classes as a separate module which can be bundled to as a 'jar' file. We can create a separate project module for this purpose. In my sample application, it has 'shims-core' module. This is a child module of our parent project which we already created. 

To create child jar module.

Step 01: Right click on the parent project, Go to New-->Project. Select the 'Maven Module' from the 'Maven' category. As you can remember, we selected 'Maven Project' when we create our parent project before. Since we are creating a project module now, we should select 'Maven Module'. 

Click the 'Next' button. You will get 'New Maven Module' window as follows.



Make sure to check 'Create a simple project' check box and give a module name. You can see that 'shims' which is our parent project has automatically selected as the 'Parent Project'. We are in a correct way. Click the 'Next' button. You will get familiar window as follows.


As You can see in the right side image, 'Group Id', 'Artifact Id' and 'Version' have already been set by default. We don't need to change any of those three field values. Keep those values as it is. Since we are creating 'jar' module, we should select 'jar' as the packaging. Click the 'Finish' button and see maven is creating our first child module.

Now refresh the parent project and notice that maven has created a new project 'shims-core' inside the 'shims'. The 'shims-core' represents a another maven project module which has separate pom.xml file. Also 'shims-core' represents as the new project within eclipse.

Just have a look on following image.

Now, If you see parent project's pom.xml file, you can see that 'shims-core' has listed under modules which says that 'shims-core' is a child module of 'shims' parent project.

  <modules>
   <module>shims-core</module>
  </modules>

Also, in the child project's pom.xml file, it has a reference to parent project. Further, in child module's pom.xml file, 'groupId' and 'version' have not been defined. Those are inherited from parents project. This is good practice when you are creating multi module project with maven.
  <parent>
    <artifactId>shims</artifactId>
    <groupId>com.semika</groupId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>..</relativePath>
  </parent>


Creating child war module

Next, we will create our web module, 'shims-web' which contains our controller classes and other view related stuff. Creating war module with m2eclipse is little different rather creating jar module. We have to use one of the maven pre-defined archetype  for web module creation. Don't worry, I will give you step by step explanation about creating maven war module. 

Step 01: Right click on parent project, Go to New-->Project and select 'Maven Module', Click 'Next' button.

In this case, we are not going to create a simple project. So don't check on 'Create simple project' check box. As you can see from the right side image, parent project has automatically selected as 'shims' which is our parent project. You have to specify the module name as 'shims-web'. and click 'Next' button.




Step 02: Click the 'Next' button from the above image. We are going to use 'maven-archetype-webapp' artifact to create our war module. See the bellow image.


Step 03: Click 'Next'  button from above window and you will get the following window.


You only need to change the 'Package' from right side window. Click the 'Finish' button and let the maven create our web module. After creating the new web module, refresh the parent project and notice that it has a new module as 'shims-web'. So now we have a multi module project with tow modules. Open pom.xml file from the web module and make sure to remove 'groupId' and 'version'. Also see the parent project's pom.xml file and notice that 'shims-web' has been added to it as a new module.

We hope to deploy our application as a war bundle into the server. If you want to change you war file name, you can change 'filaName' attribute of pom.xml file of 'shims-web' module.


  <build>
    <finalName>shims-web</finalName>
  </build>

This will create 'shims-web.war' file under 'target' directory of 'shims-web' module. Next, we need to define dependencies between 'shims-core' and 'shims-web' module. This will force the maven reactor to build 'shims-core' module before building 'shims-web' module when we run maven build from the parent project. Because, we want to bundle our business logic and DAO related classes into a separate jar module and give it to web module as a separate library along with other libraries, core module should be build before web module.

Place the following contents inside the 'dependencies' section of pom.xml file of 'shims-web' module.

    <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>shims-core</artifactId>
        <version>${project.version}</version>
    </dependency>

${project.groupId} and ${project.version} are two global references of maven which provides 'groupId' and 'version' across the whole modules of the multi module project. We can directly use it.

Building multi module project

There are lots of advantages of creating multi module project. We can build the entire project at once and also we can build each module separately. Developers can separately work on different modules at the same time without depending on others. Other thing is, we have clear separation which enhance maintainability.
We have to create maven targets to build our project. Right click on parent project. Go to Run As --> Maven Build. You will get the following window only for the very first time. There you can create a new target to build your project. 
The right side window allow you to create maven target to build your project. You have to give a name for your build target and browse the required project module directory. Since I am going to create this maven target to build the entire project, I have located my parent directory. Put 'clean install' into the Goals field.
Since, we are going to build our project very first time, we should allow maven to download the required dependencies from their global repository. Make sure 'offline' check box 'unchecked'. After first success build, you can update this target to run it in offline mode by selecting 'offline' check box.
Make sure 'Maven Runtime' has been set to our maven installation directory. Otherwise you will get build errors.

OK!. That's it. You are ready to build your first maven multi module project. Good luck!

Click on 'Apply' and then 'Run' button. Maven will start the building project. See carefully eclipse console. It says full story. You should get 'SUCCESS' message at the end of the build. Similar output as follows.

-------------------------------------
[INFO] Installing /home/vinesh/workspace-maaven/shims/shims-web/pom.xml to /home/vinesh/.m2/repository/com/semika/shims-web/1.0-SNAPSHOT/shims-web-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] [INFO] shims ............................................. SUCCESS [1.093s]
[INFO] shims-core ........................................ SUCCESS [2.005s]
[INFO] shims-web Maven Webapp ............................ SUCCESS [1.027s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.354s
[INFO] Finished at: Sat Nov 03 22:37:27 IST 2012
[INFO] Final Memory: 6M/103M
[INFO] ------------------------------------------------------------------------

Let's deploy our application. I am using Apache tomcat7 to deploy my application. If you see inside the target directory of 'shims-web' module after the successful build, you can see,maven has create 'shims-web.war' file. Copy that file into tomcat's webapps folder and start the tomcat.

Point your browser the following URL.

http://localhost:8080/shims-web/

You should get hello world page. 


You may also like:

Thursday, November 1, 2012

Full stack trace for "SEVERE: Error listenerStart"

One of the very painful error, I have received when starting the tomcat 'SEVERE: Error listenerStart'. I can not even understand why tomcat is giving errors in such a poor manner by wasting lots of our time. This is server error, but it does not give enough information to get rid of that. 

As it says, this may be some configuration problems which failed creation of some listener. For example, it may be some problem with your spring configuration which prevents creating ContextLoaderListener.

Sometime, this may be a problem of misconfiguration of a filter. You may have configured a filter in web.xml, but the relevant filter class is not found. 
Most of you will happy, if we can get the full stack trace of above exception.You can create 'logging.properties' file in your project source so that, it will be in 'classes' folder  of your deployment archive. You can place it within 'src' folder or within 'resources' folder in your source based on your project type. The project may be tomcat project or maven project or dynamic web project. 

After creating that file, place the following contents within it.

org.apache.catalina.core.ContainerBase.[Catalina].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].handlers = java.util.logging.ConsoleHandler

Now try to start the tomcat and you will see full stack trace of above poor indication.
You may also like:

Saturday, October 27, 2012

How to place the Facebook "Like Box" to your web site or Blog?

One of the very useful social plugin provided by the Facebook is 'Like Box'. If you have placed Facebook like box in your website or blog, then your visitors can make 'likes' to it. This will not be a partial tutorial. I don't want to leave any thing unclear about this. At the end of this tutorial, you must be able to add Facebook like box into your website or blog.

Just look at the right side bar of my blog. You can see Facebook like box there. Unfortunately by this time, my wife and my self has liked to my Blog. I hope, you will also like my blog after reading this tutorial.

OK, let's move to real topic!

Go to this URL,

http://developers.facebook.com/docs/plugins/


There you can see all the plugins available with Facebook. Just scroll down and you can see 'Like Box' plugin for you. Click on that link and you will get the following form.


I guess, most of the fields of the right side form are clear for you. But the first field which is 'Facebook Page URL', confuses us. Some people think, this should be your blog or website URL and some are thinking, this should be a Facebook's application URL. So they create new Facebook application and try to get the Facebook like box. Actually as it says, it should be the Facebook page URL. You have to create a Facebook page so that your website or blog will get likes via that page. Facebook 'Like Box' is the plugin which connects Facebook page with our website or blog. We don't actually want visitors to visit Facebook page, but we want, visitors should visit our website or blog. The ultimate target is, we want to allow our visitors of blog or website to give their impression on it by giving a 'likes'.

As the first step, let,s create Facebook page first. Login to your Facebook account and click on 'Like Pages' link on left side bar under PAGES category of Facebook home page. You will get the following page.



You can see 'Create a Page' green color button on right top area of the page. Click on that and you will get the following page.


You can see several categories on this page. Select the one appropriate for you and provide the required details. It is important to provide unique name for your page, because visitors will see that on website or blog. I selected 'Company, Organization or Institution' and my page name is 'Lasanthiblog'. You can see it from bellow image.


After providing a good name for your page, proceed by clicking on 'Get Started' button. You will get the following page which is very important.

As you can see from this image, we have to complete four steps to create our Facebook page. As the first step upload a picture for your Facebook page profile and click 'Save Photo' button. You will be directed to second step. See the picture bellow.


You have to provide some description about your page and URL of your website or blog. I have provided my blog's URL as you can see. Go to next step by clicking on 'Save Info' button. Next page will be displayed as follows.


This step is very important. You will get the public URL of your page from here. You can select proper and meaningful name for your page URL. As you can see, I have given 'lasanthiblog' as page address. Click on 'Set Address' button and you can skip the next step if you don't want to enable adds.

After completing the 4th step, you will be redirected to your page. My page is shown bellow. Actually this is not my real page, I created this page to demonstrate, how to create a Facebook page for you.



Now the hard part is done and you are ready to get Facebook like plugin for your website or blog. Now we have Facebook page RUL.

http://www.facebook.com/lasanthiblog

Now the time to get code for our Facebook like box plugin. Again go to the Facebook plugin page.

http://developers.facebook.com/docs/plugins/

Select 'Like Box' and fill the form. After filling the form click on 'Get Code' button. You can get the code in four different ways. But I suggest you to get 'IFRAME' code.

Now you place this code in your website or blog, where you want to place Facebook like plugin. I guess, you can manage that by your selves. I hope this will be helpful for you and if you are still having problem of getting Facebook 'Like Box', please mail to me. I will help you. OK!, go ahead.

You may also like:

Friday, October 26, 2012

[ERROR] error: error reading *.jar; invalid LOC header (bad signature) - Maven build error

Some time you will get the following error when building a project with maven. Most of the time, this happens after checking out new maven project and try to build the project with an existing repository. Some existing jar files can not be read when building the new project. This may happen because, you may already have some projects sharing the same repository.

The following shows a sample error.

[ERROR] error: error reading /home/vinesh/.m2/repository/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar; invalid LOC header (bad signature)

As a solution for this, you can remove the existing jar file and build the project again in online mode. You can use the following command.

mvn clean install -Dmaven.test.skip=true

Now, carefully look into the console.You can see, it downloads the removed jar file from the maven repository.
You may also like:

Sunday, October 21, 2012

Make your all AJAX request through a single gateway.

AJAX has become a most popular and widely used technology in modern web application. In some web application, around 95% of the server requests are AJAX requests. When you are developing your application, it is always a good practice finding more generic and reusable ways of implementing things. Sometime, we may call as inventing new patterns. 

This tutorial explains a pattern of making your all AJAX requests through a single gateway. I am not explaining a technology feature, but this is programing technique. 

What are the advantages?

  • Suppose you want to pass some common set of parameters with every AJAX request. This can be easily achieved by this technique. 
  • If your application has an exception flow mechanism which brings exception messages happened in your DAO layer or service layer or even in the controller layer into the client view, those can be easily handled in central point with this technique. 
  • If you want to show each operations status to the user, that can also be handled from a central point.
There may be more advantages.I will give you a sample implementation with jQuery. But this can be implemented with any Javascript library as you wish.

If you use jQuery, the following is the typical Javascript method using for AJAX request.

$.ajax({
        type: 'POST',
        url: CONTEXT_ROOT + "/changePassword.htm",
        data: $.blimp.serializeForm("frmChangePwd"),
        success: function (response) {
            //handle success
        },
        error: function () {
            //handle errors
        },
        dataType: 'json'
});


For my application, I have written a common central method to make every AJAX requests. This method works as a gateway which all AJAX requests are passing through this method. Any common functionality that we need to add before making the request as well as after making the request, can easily be added to this common method. The following code shows the common gateway method.


/**
 * @author Semika siriwardana
 * 
 */
(function($) {
     $.myObj = {
  /**
   * Central AJAX caller gateway.
   */
 ajax: function(objConfig) {
   
     $.ajax({
     type: objConfig.type, //mandatory field
     url: objConfig.url, //mandatory
     headers: (objConfig.headers != undefined)?objConfig.headers:{}, 
     async: (objConfig.async != undefined)?objConfig.async:true,
     data : (objConfig.data != undefined)?objConfig.data:{},
     dataType : (objConfig.dataType != undefined)?objConfig.dataType:'json',
     cache: (objConfig.cache != undefined)?objConfig.cache:true,
     contentType: (objConfig.contentType != undefined)?objConfig.contentType:'application/x-www-form-urlencoded',
       
           success: function(data) {
               //Handle success status and do more
               objConfig.success(data); //Invoke success callback method.   
           },
             
           error: function(data, textStatus, jqXHR) {
                       //Handle error status and do more
                objConfig.error(data, textStatus, jqXHR)   
           }
     });
 }
     }
}(jQuery));

As I mentioned before, this is a programing technique or a pattern. You know that '$.ajax()' is the method provided by jQuery for AJAX request. As you see above, I have written a wrapper method as '$.myObj.ajax(objConfig)'. Now onwords, when you want to make any AJAX requests, you are not directly invoking original '$.ajax()' method, but you invoke the wrapper method '$.myObj.ajax(objConfig)'. 

Next, you will have a problem. What is this 'objConfig' parameter passing into '$.myObj.ajax()' method?. You will get the answer soon.
To make your AJAX request through our gateway method, you just need to change '$.ajax' to '$.myObj.ajax' from the above code.

myObj.ajax({
       type: 'POST',
       url: CONTEXT_ROOT + "/changePassword.htm",
       data: $.blimp.serializeForm("frmChangePwd"),
       success: function (response) {
            //handle success
       },
       error: function () {
            //handle errors
       },
       dataType: 'json'
});

If you use '$.myObj.ajax' for your all AJAX requests, you can easily handle some common behaviours with AJAX invocation.

You may also like:

Monday, October 15, 2012

Facebook similar chat for your Java web application.

Chatting is easy just like eating a piece of cake or drinking a hot coffee. Have you ever thought about developing a chat program by yourself?. You know that, it is not easy as chatting. But, if you are a developer and if you read to the end of this article, you may put a try to develop a chatting application by your self and allow your users to chat via your web application. 

I had to implement a chatting application for my web application. As every one does, I started to search on internet. I found IRC. When I read and search more about IRC, I understood that finding a web base client for IRC was difficult. I wanted to have more customizable web client which is working similar to Facebook. At last and luckily, I found CometD. 

Finally, I was able to implement chatting application by using CometD and more customizable chat windows opening on the browser which is exactly similar to Facebook. This works almost all the modern browsers. This article explains step by step, How to implement chatting application from the scratch and also How to integrate chatting application to your existing Java base web application. Remember, Your web application should be a Java base one.

You need to download the cometD from their official web site. It has all the dependencies required to implement the chatting application except tow java script libraries. I have written two Javascript libraries, one to create dynamic chat windows like Facebook and other to handle CometD chatting functionality in generic way. If you can manage these stuff by your self, you don't need to use those tow Javascript libraries. Actually, CometD documentation provides good details. But, I go ahead with the tutorial by using those tow libraries. Any way, I recommend first use those tow libraries and then customize it as you need. I hope to share the sample application with you and you can deploy it in your localhost and test, how it works.

1.Adding required jar files. 

If you use maven to build your project, add the following dependencies into your pom.xml file

<dependencies>
    <dependency>
        <groupId>org.cometd.java</groupId>
        <artifactId>bayeux-api</artifactId>
        <version>2.5.0</version>
    </dependency>
    <dependency>
         <groupId>org.cometd.java</groupId>
         <artifactId>cometd-java-server</artifactId>
         <version>2.5.0</version>
    </dependency>
    <dependency>
         <groupId>org.cometd.java</groupId>
         <artifactId>cometd-websocket-jetty</artifactId>
         <version>2.5.0</version>
         <exclusions>
             <exclusion>
                 <groupId>org.cometd.java</groupId>
                 <artifactId>cometd-java-client</artifactId>
             </exclusion>
         </exclusions>
   </dependency>
   <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.6.6</version>
   </dependency>
   <dependency>
        <groupId>org.cometd.java</groupId>
        <artifactId>cometd-java-annotations</artifactId>
        <version>2.5.0</version>
   </dependency>
</dependencies>

If you are not using maven to build your project, just copy the following .jar files into /WEB-INF/lib folder from your CometD download bundle. You can find these .jar files from /cometd-demo/target/cometd-demo-2.5.0.war file.

  • bayeux-api-2.5.0.jar
  • cometd-java-annotations-2.5.0.jar
  • cometd-java-common-2.5.0.jar
  • cometd-java-server-2.5.0.jar
  • cometd-websocket-jetty-2.5.0.jar
  • javax.inject-1.jar
  • jetty-continuation-7.6.7.v20120910.jar
  • jetty-http-7.6.7.v20120910.jar
  • jetty-io-7.6.7.v20120910.jar
  • jetty-jmx-7.6.7.v20120910.jar
  • jetty-util-7.6.7.v20120910.jar
  • jetty-websocket-7.6.7.v20120910.jar
  • jsr250-api-1.0.jar
  • slf4j-api-1.6.6.jar
  • slf4j-simple-1.6.6.jar

2.Adding required Javascript files.

You need to link the following Javascript files.

  • cometd.js
  • AckExtension.js
  • ReloadExtension.js
  • jquery-1.8.2.js
  • jquery.cookie.js
  • jquery.cometd.js
  • jquery.cometd-reload.js
  • chat.window.js
  • comet.chat.js
The 'chat.window.js' and 'comet.chat.js' are my own tow Javascript libraries which does not come with CometD distribution. If you are totally following this tutorial, you have to link those tow libraries as well. Provided sample application has these tow Javascript libraries. 

3.Writing chat service class.

/**
 * @author Semika siriwardana
 * CometD chat service.
 */
package com.semika.cometd;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.inject.Inject;

import org.cometd.annotation.Configure;
import org.cometd.annotation.Listener;
import org.cometd.annotation.Service;
import org.cometd.annotation.Session;
import org.cometd.bayeux.client.ClientSessionChannel;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ConfigurableServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.authorizer.GrantAuthorizer;
import org.cometd.server.filter.DataFilter;
import org.cometd.server.filter.DataFilterMessageListener;
import org.cometd.server.filter.JSONDataFilter;
import org.cometd.server.filter.NoMarkupFilter;

@Service("chat")
public class ChatService {
    private final ConcurrentMap<String, Map<String, String>> _members = new ConcurrentHashMap<String, Map<String, String>>();
    @Inject
    private BayeuxServer _bayeux;
    @Session
    private ServerSession _session;

    @Configure ({"/chat/**","/members/**"})
    protected void configureChatStarStar(ConfigurableServerChannel channel) {
        DataFilterMessageListener noMarkup = new DataFilterMessageListener(new NoMarkupFilter(),new BadWordFilter());
        channel.addListener(noMarkup);
        channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
    }

    @Configure ("/service/members")
    protected void configureMembers(ConfigurableServerChannel channel) {
        channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
        channel.setPersistent(true);
    }

    @Listener("/service/members")
    public void handleMembership(ServerSession client, ServerMessage message) {
        Map<String, Object> data = message.getDataAsMap();
        final String room = ((String)data.get("room")).substring("/chat/".length());
        
        Map<String, String> roomMembers = _members.get(room);
        if (roomMembers == null) {
            Map<String, String> new_room = new ConcurrentHashMap<String, String>();
            roomMembers = _members.putIfAbsent(room, new_room);
            if (roomMembers == null) roomMembers = new_room;
        }
        final Map<String, String> members = roomMembers;
        String userName = (String)data.get("user");
        members.put(userName, client.getId());
        client.addListener(new ServerSession.RemoveListener() {
            public void removed(ServerSession session, boolean timeout) {
                members.values().remove(session.getId());
                broadcastMembers(room, members.keySet());
            }
        });

        broadcastMembers(room, members.keySet());
    }

    private void broadcastMembers(String room, Set<String> members) {
        // Broadcast the new members list
        ClientSessionChannel channel = _session.getLocalSession().getChannel("/members/"+room);
        channel.publish(members);
    }

    @Configure ("/service/privatechat")
    protected void configurePrivateChat(ConfigurableServerChannel channel) {
        DataFilterMessageListener noMarkup = new DataFilterMessageListener(new NoMarkupFilter(),new BadWordFilter());
        channel.setPersistent(true);
        channel.addListener(noMarkup);
        channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
    }

    @Listener("/service/privatechat")
    protected void privateChat(ServerSession client, ServerMessage message) {
        Map<String,Object> data = message.getDataAsMap();
        
        String room = ((String)data.get("room")).substring("/chat/".length());
        Map<String, String> membersMap = _members.get(room);
        if (membersMap == null) {
            Map<String,String>new_room=new ConcurrentHashMap<String, String>();
            membersMap=_members.putIfAbsent(room,new_room);
            if (membersMap==null)
                membersMap=new_room;
        }
        
        String peerName = (String)data.get("peer");
        String peerId = membersMap.get(peerName);
        
        if (peerId != null) {
            
         ServerSession peer = _bayeux.getSession(peerId);
            
            if (peer != null) {
             Map<String, Object> chat = new HashMap<String, Object>();
                String text = (String)data.get("chat");
                chat.put("chat", text);
                chat.put("user", data.get("user"));
                chat.put("scope", "private");
                chat.put("peer", peerName);
                ServerMessage.Mutable forward = _bayeux.newMessage();
                forward.setChannel("/chat/" + room);
                forward.setId(message.getId());
                forward.setData(chat);

                if (text.lastIndexOf("lazy") > 0) {
                    forward.setLazy(true);
                }
                if (peer != client) {
                    peer.deliver(_session, forward);
                }
                client.deliver(_session, forward);
            }
        }
    }

    class BadWordFilter extends JSONDataFilter {
        @Override
        protected Object filterString(String string) {
            if (string.indexOf("dang") >= 0) {
                throw new DataFilter.Abort();
            }
            return string;
        }
    }
}

4.Changing web.xml file.

You should add the following filter into your web.xml file.

<filter>
     <filter-name>continuation</filter-name>
     <filter-class>org.eclipse.jetty.continuation.ContinuationFilter</filter-class>
</filter>
<filter-mapping>
     <filter-name>continuation</filter-name>
     <url-pattern>/cometd/*</url-pattern>
</filter-mapping>

And also the following servlet.

<servlet>
    <servlet-name>cometd</servlet-name>
    <servlet-class>org.cometd.annotation.AnnotationCometdServlet</servlet-class>
    <init-param>
         <param-name>timeout</param-name>
         <param-value>20000</param-value>
    </init-param>
    <init-param>
         <param-name>interval</param-name>
         <param-value>0</param-value>
    </init-param>
    <init-param>
         <param-name>maxInterval</param-name>
         <param-value>10000</param-value>
    </init-param>
    <init-param>
         <param-name>maxLazyTimeout</param-name>
         <param-value>5000</param-value>
    </init-param>
    <init-param>
         <param-name>long-polling.multiSessionInterval</param-name>
         <param-value>2000</param-value>
    </init-param>
    <init-param>
         <param-name>logLevel</param-name>
         <param-value>0</param-value>
    </init-param>
    <init-param>
         <param-name>transports</param-name>
         <param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
    </init-param>
    <init-param>
         <param-name>services</param-name>
         <param-value>com.semika.cometd.ChatService</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>cometd</servlet-name>
    <url-pattern>/cometd/*</url-pattern>
</servlet-mapping>

5.Implementing client side functions.

I think this section should be descriptive. If you allows your users to chat with other users, you need to show the list of online users in you web page, just like Facebook shows the online users inside the right side bar. For that, you can place a simple <span> or <div> tag inside your page. I have done it as follows.
<div id="members"></div>

All the online users will be displayed with in the above container. Once you click on a particular user name, it will open a new chat window similar to Facebook. For each pair of users, it will open a new chat window. To get this behaviour, you should use 'chat.window.js' which I mentioned before. Chatting in between particular pair of users will continue through a dedicated chat window. 

Just after user is logging into your web application as usual way, we should subscribe that user to chat channels. You can do it using the following way. 

 
$(document).ready(function(){ 
    $.cometChat.onLoad({memberListContainerID:'members'});
});

Note that, I have passed the 'id' of online user list container as a configuration parameter. Then, user should be joined with channel as follows.You can call the bellow method with the username.
function join(userName){
   $.cometChat.join(userName);
}

Since for each chat, there is a dedicated chat window just like Facebook, we should maintain global Javascript array to store those created chat window objects. You need to place the following Javascript code inside your page.
function getChatWindowByUserPair(loginUserName, peerUserName) {
    var chatWindow;  
    for(var i = 0; i < chatWindowArray.length; i++) {
       var windowInfo = chatWindowArray[i];
       if (windowInfo.loginUserName == loginUserName && windowInfo.peerUserName == peerUserName) {
           chatWindow =  windowInfo.windowObj;
       }
    }
    return chatWindow;
}
 
function createWindow(loginUserName, peerUserName) {  
    var chatWindow = getChatWindowByUserPair(loginUserName, peerUserName);
    if (chatWindow == null) { //Not chat window created before for this user pair.
       chatWindow = new ChatWindow(); //Create new chat window.
       chatWindow.initWindow({
             loginUserName:loginUserName, 
             peerUserName:peerUserName,
             windowArray:chatWindowArray});
   
       //collect all chat windows opended so far.
       var chatWindowInfo = { peerUserName:peerUserName, 
                              loginUserName:loginUserName,
                              windowObj:chatWindow 
                            };
   
       chatWindowArray.push(chatWindowInfo);
   }
   chatWindow.show();  
   return chatWindow;
}

As I mentioned above, declare following global Javascript variable.
var chatWindowArray = [];   
var config = {
    contextPath: '${pageContext.request.contextPath}'
};

Since I am using a JSP page, I have to get the context path via 'pageContext' variable. If you are using a HTML page, manage it by your self to declare 'config' Javascript global variable.
Now, you almost reached to last part of the tutorial.

5.How does the sample application works?

You can download the comet.war file and deploy it in your server. Point the browser to following URL.

http://localhost:8080/comet

This will bring you to a page which has a text field and button called "Join". Insert some user name as you wish and click on "Join" button. Then you will be forwarded to a another page which has list of online users. Your name is highlighted in red color. To chat in your local machine, You can open another browser (IE and FF) and join to the chat channel. The peer user displays in blue color in the online users list. Once you click on a peer user, it will open a new chat window so that You can chat with him. This functions very similar to Facebook chatting. 

I have tested this chatting application in IE, FF and Crome and works fine. If you want any help of integrating this with your Java base web application, just send me a mail.


You may also like:
Share

Widgets