Wednesday, August 11, 2010

Wicket Ajax Session time out problem

Wicket is super cool Web framework. Doing Ajax stuff is even more cooler. I just love saying cool , kool , kewl.

Most/All of the applications using wicket need a way to handle session time out with Ajax request.So I thought of putting it on my blog.



Wicket life cycle method made everything very easy and that's just kool(Cant help my self)

In just 2 simple steps you can handle Ajax session time out problem.

So here what you need to do:

Step 1:
First override newRequestCycle method in your webApplication class. Like below



public RequestCycle newRequestCycle(Request request, Response response) {

// TODO Auto-generated method stub

return new CustomRequestCycle(this, (WebRequest)request, response);

}


Step 2:

Create new CustomRequestCycle class which extends WebRequestCycle like below


public class CustomRequestCycle extends WebRequestCycle{



public CustomRequestCycle(final WebApplication application, final WebRequest request, final Response response) {

super(application, request, response);

}

@Override

public final Page onRuntimeException(final Page cause, final RuntimeException e) {

// obviously you can check the instance of the exception and return the appropriate page if desired

if (e instanceof PageExpiredException) {

if ( this.getWebRequest().isAjax()) {

return new YourSignonPage(new PageParameters());

}

}

return new YourCustomErrorPage(e);

}

}


See how I override onRuntimeException method. Just check for PageExpiredException and if its Ajax request , return your Sign On Page.

That's it. Kool :)

Thursday, September 17, 2009

Jquery grid with Jersey Rest full Service

Recently i got chance to explore Jquery grid. Looks pretty kool to me. I tried to integrate that with Jersey restfull web service.

If you want to search data and want to show that in Jquery grid, then below sample might help.





First you need to include these css and js files in your code. Download then from Jquery and Jgrid site(Google that).
  
<link rel="stylesheet" type="text/css" media="screen" href="css/jquery-ui-1.7.1.custom.css" />
<link rel="stylesheet" type="text/css" media="screen" href="css/ui.jqgrid.css" />
<script src="js/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="js/grid.locale-en.js" type="text/javascript"></script>
<script src="js/jquery.jqGrid.min.js" type="text/javascript"></script>
<script src="js/jquery.json-2.2.min.js" type="text/javascript">
Html Code for search and Jquery Grid
  
<form name="search" id="search" method="post">
<table >

    <tr>
      <td> Brand</td>
        <td> <select name="brand" id="brand"><option value="">Select</option>
          <option value="b1">Brand 1</option>
          <option value="b2">Brand 2</option>
        <option value="b3">Brand 3</option>
           </select></td>
     </tr>
     <tr>
        <td> Medical Topics</td>
        <td> <select id="medicalTopic" name="medicalTopic"><option value="">Select</option>
              <option value="t1">Topic 1</option>
        <  option value="t2">Topic 2</option>
          <option value="t3">Topic 3</option>
      </select></td>
    </tr>
    <tr>
      <td> Search </td>
      <td> <input type="text" id="searchField" name="searchField"> </td>
    </tr>
    <tr>
      <td colspan="2" align="right"> <input type="button" id="searchButton" value="Search"></td>

    </tr>
</table>

</form>

<div id="dataDiv">
    <table id="list2" class="scroll" cellpadding="0" cellspacing="0"></table>
    <div id="pager2" class="scroll" style="text-align:center;"></div>
</div>
Java script code for Jquery Grid
  

jQuery("#list2").jqGrid({datatype: "json",
colNames:['id','Name', 'Description', 'Checkout', 'CheckIn', 'File'],
colModel:[ {name:'id', width:55},
  {name:'name',width:100},
   {name:'description',width:100},
   {name:'fileName',width:100, editable: true, formatter:checkout},
   {name:'fileName',width:100,editable: true, formatter:checkIn },
   {name:'fileName',width:150, editable: true, formatter:fileLink, align:"right"} ],
rowNum:10,
rowList:[10,20,30],
imgpath: 'css/images',
pager: jQuery('#pager2'),
sortname: 'id',
jsonReader: { repeatitems : false, id: "0" },
viewrecords: true,
sortorder: "desc",
caption:"Search Result" }).navGrid('#pager2',{edit:false,add:false,del:false});

jQuery("#dataDiv").hide();
Java script code for search button
  
jQuery("#searchButton").click(function(){
var dataString = 'searchField='+ $("#searchField").val() + '&brand=' + $("#brand").val() + '&medicalTopic=' + $("#medicalTopic").val() ;
url = "resources/searchData?" + dataString;
jQuery("#list2").clearGridData();

jQuery.getJSON(url,
function(data) {
  if (data.records == 0) {
    jQuery("#dataDiv").hide();
  } else {
    var mygrid = jQuery("#list2")[0];
    mygrid.addJSONData(data);
    myjsongrid = null;
    jQuery("#dataDiv").show();

  }
});

});
We need to create formatters in Jquery Grid to make links and buttons in grid.
  


checkout = function (el, cellval, opts){
  return '<img src="css/images/checkOut.png" onclick=alert("clicked")>';
}

checkIn = function (el, cellval, opts){
  return '<img src="css/images/checkIn.png" onclick=alert("clicked")>';
}

fileLink = function (el, cellval, opts){
  return '<a href="javascript:void(0)" onClick=downloadFile("' + opts.fileName +'")>' + opts.fileName + '</a>';
}

// Funtion to download files from server.
function downloadFile(fileName){
  // Create an IFRAME.
  var iframe = document.createElement("iframe");
  iframe.src = "resources/DownloadFile?name=" + fileName;
  iframe.style.display = "none";
  document.body.appendChild(iframe);
}
Server Side Code
Jersey rest ful services to get Data for our grid
  

@Path("searchData")
public class SearchDataResource {

    @Context
    private UriInfo context;

    /** Creates a new instance of SearchDataResource */
    public SearchDataResource() {
    }


    @GET
    @Produces("application/json")
    public ItemBean getData(@Context HttpServletRequest request) {
        SearchCriteria criteria = getSearchCriteria(request);
        ItemBean bean = new ItemBean();
        bean.setRows(search(criteria));
        return bean;
    }

    private SearchCriteria getSearchCriteria(HttpServletRequest request) {
        SearchCriteria criteria = new SearchCriteria();
        criteria.setKeyword(request.getParameter("searchField"));
        criteria.setBrand(request.getParameter("brand"));
        criteria.setTopic(request.getParameter("medicalTopic"));
        return criteria;
    }




    public List search(SearchCriteria criteria){
        List items = new ArrayList();
        if(criteria.getBrand() != null && criteria.getBrand().length() > 0){
                for(Item item : getRecords()){
                         if(item.getName().equalsIgnoreCase(criteria.getBrand())){
                            items.add(item);
                        }
                }
        }else{
                items.addAll(getRecords());
        }

        return items;

    }

    private List getRecords(){
        List records = new ArrayList();
        Item item1 = new Item();
        item1.setId(1);
        item1.setName("b1");
        item1.setDescription("test data 1");
        item1.setFileName("File1.txt");
        Item item2 = new Item();
        item2.setId(2);
        item2.setName("b2");
        item2.setFileName("File2.txt");
        item2.setDescription("test data 2");
        Item item3 = new Item();
        item3.setId(3);
        item3.setName("b2");
        item3.setFileName("File3.txt");
        item3.setDescription("test data 3");
        records.add(item1);
        records.add(item2);
        records.add(item3);
        return records;
    }


}
Search Criteria class
  

public class SearchCriteria {

    private String brand;
    private String topic;
    private String keyword;

    getter and setters......
    .................
}
Item Bean Class
  

@XmlRootElement
public class ItemBean {

    // "rowa" name should be same
    private Collection rows = new ArrayList();

    public Collection getRows() {
        return rows;
    }

    public void setRows(Collection rows) {
        this.rows = rows;
    }

}

Item Class
Item Class
  

@XmlRootElement
public class Item {

    private long id;
    private String name;
    private String description;
    private String fileName;


    getter and setters......
    .................

}
Bonus Bonus Bonus , If you have reached upto here

Woo Hoo Download file with Jersey web service

Download Rest Full Web service code
  

@Path("DownloadFile")
public class FileDownloadResource {

    @Context
    private UriInfo context;

    /** Creates a new instance of DownloadFileResource */
    public FileDownloadResource() {
    }

    @GET
    @Produces("application/xml")
    public String getXml(@Context HttpServletResponse response, @Context HttpServletRequest request) {
        String fileName = request.getParameter("name");

        response.setContentType("application/x-download");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);


        OutputStream out = null;
        try {
            out = response.getOutputStream();
            byte[] bbuf = new byte[1024];
            File file = new File("C:\\test\\" + fileName);
            DataInputStream in = null;
            int length = 0;
            in = new DataInputStream(new FileInputStream(file));
            while ((in != null) && ((length = in.read(bbuf)) != -1)) {
                    out.write(bbuf, 0, length);
            }
        } catch (IOException ex) {
            // Cry and call your mom
        }



        return "";

    }
}
TODO - Try sorting and pagination with Grid.


Sunday, March 15, 2009

Hibernate Unique validator with Seam



Have you ever wished , if your favorite framework provides some kind of functionality that validates unique constraints against your Database tables.

Not until now , most of us were catching exceptions and look for Sql error code for unique constraint or doing some kind of work around to handle unique constraint issues. Which is not so clean and nice looking.

Fortunately, with JBoss Seam and Hibernate validator we can handle unique constraint problem in a elegant way. And did i say that it will be Generic too. Yes it will work for all of your tables.

Lets see how Jboss Seam will handle Unique constraint problem and returns a nice looking message.

Suppose we have a table called Users with userName and some other property. Do i need to say userName should be unique.


CREATE TABLE Users(
id INT NOT NULL AUTO_INCREMENT
, userName VARCHAR(200) NOT NULL
, password VARCHAR(15) NOT NULL
....
//Some more properties
);

Before looking at User domain class , lets create an annotation called @Unique


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ValidatorClass(UniqueValidator.class)
public @interface Unique {
String message() default " already exists";

String entityName();

String fieldName();
}

@Unique is a simple annotation with 2 properties
entityName - your mapped class
fieldName - property that should be unique for this entity.

And UniqueValidator class



@Name("UniqueValidator")
public class UniqueValidator implements Validator, PropertyConstraint {

//Entity for which validation is to be fired
private String targetEntity;

//Field for which validation is to be fired.
private String field;

public void initialize(Unique parameters) {
targetEntity = ((Unique)parameters).entityName();
field = ((Unique)parameters).fieldName();
}

public boolean isValid(Object value) {
EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
Query query = entityManager.createQuery("select t from " + targetEntity
+ " t where t." + field + " = :value");
query.setParameter("value", value);

try {
query.getSingleResult();
return false;
} catch (final NoResultException e) {
return true;
}
}

public void apply(Property arg0) {

}
}



Now we just have to apply this @Unique annotation to User class




public class User {

@Unique(entityName = "come.XXX.User", fieldName = "userName")
protected String userName;

protected String password;

............

}



Add <s:validateall> in your view

<s:validateall>
<h:outputtext value="User Name">

<ice:inputtext partialsubmit="true" id="ApplicationUser_userName" required="true" value="#{user.userName}">
</ice:inputtext>

<h:message styleclass="error errors" for="ApplicationUser_userName">
</h:message>
</h:outputtext>
</s:validateall>



That's it!! Now Seam will auto magically show you "already exist" message as you will tab out of user name text box. Thats Seam Magic.