Saturday, December 1, 2012

RESTful with RESTEasy - Part 2/4 (Client Consuming a POST and GET RESTful Service)

Overview:
This post looks at how to write a service client using RESTEasy framework, which is an excellent framework for developing RESTful services, and has gained a lot of popularity in the Mobile application development world.

Also Refer to,
RESTful with RESTEasy - Part 1/4 (Creating a POST and GET RESTful Service)

You can find the sample code here.

Prerequisite:JDK >= 1.5.x
Maven >= 2.2.x


Step 1: Maven Dependencies (pom.xml)
Update your client project's pom.xml by adding the following RESTEasy dependency.

 <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/maven-v4_0_0.xsd">  
   . . .
   <dependencies>  
     . . .  
     <dependency>  
       <groupId>org.jboss.resteasy</groupId>  
       <artifactId>resteasy-jaxrs</artifactId>  
       <version>${resteasy.version}</version>  
     </dependency>  
    . . .
   </dependencies>  
   . . .
 </project>  

Step 2: How to create a requests

   private static final String SERVICE_URL = "http://localhost:8080/greetingspring";  
   private static final String SERVICE_URL_ADD = "/add";  
   private static final String SERVICE_URL_UPDATED = "/update";  
   private static final String SERVICE_URL_GET = "/get/{id}";  
   private static final String SERVICE_URL_GET_ALL = "/getAll";  
   . . .  
   private static ClientRequest createRequest(String uri) {  
     ClientRequest request = new ClientRequest(SERVICE_URL + uri);  
     request.accept("application/json");  
     return request;  
   }  
   . . .  
   ClientRequest request = createRequest(SERVICE_URL_ADD)  
   ClientRequest request = createRequest(SERVICE_URL_GET)  
   ClientRequest request = createRequest(SERVICE_URL_UPDATE)  
   ClientRequest request = createRequest(SERVICE_URL_GET_ALL)  

Creating a POST request
 private static Long addUser(User newUser) throws Exception {  
     String requestJson = "{\"user\":" + JSON_CONVERTER.toJson(newUser) + "}";  
     displayRequest(requestJson);  
     ClientRequest request = createRequest(SERVICE_URL_ADD).body(CONTENT_TYPE, requestJson);  
     ClientResponse<String> response = request.post(String.class);  
     displayResponse(response, HTTP_CODE_CREATED);  
     return Long.parseLong(response.getEntity().toString());  
   }  

Creating a GET request
 private static User getUser(Long userId) throws Exception {  
     ClientRequest request = createRequest(SERVICE_URL_GET.replace("{id}", userId.toString()));  
     ClientResponse<String> response = request.get(String.class);  
     displayResponse(response, HTTP_CODE_OK);  
     String responseJson = response.getEntity().toString();  
     responseJson = responseJson.replace("{\"user\":", "").replace("}}", "}");  
     return JSON_CONVERTER.fromJson(responseJson, User.class);  
   }  

Complete Code for the Client


 package org.fazlan.resteasy.spring.client;  
 import com.google.gson.Gson;  
 import org.fazlan.resteasy.spring.entity.User;  
 import org.jboss.resteasy.client.ClientRequest;  
 import org.jboss.resteasy.client.ClientResponse;  
 import java.io.BufferedReader;  
 import java.io.ByteArrayInputStream;  
 import java.io.IOException;  
 import java.io.InputStreamReader;  

 public class RestClient {  

   private static final String SERVICE_URL = "http://localhost:8080/greetingspring";  
   private static final String SERVICE_URL_ADD = "/add";  
   private static final String SERVICE_URL_UPDATED = "/update";  
   private static final String SERVICE_URL_GET = "/get/{id}";  
   private static final String SERVICE_URL_GET_ALL = "/getAll";  
   private static final String CONTENT_TYPE = "application/json";  
   private static final Gson JSON_CONVERTER = new Gson();  
   private static final int HTTP_CODE_OK = 200;  
   private static final int HTTP_CODE_CREATED = 201;  

   public static void main(String[] args) throws Exception {  
     User newUser = new User("Firstname", "Lastname");  
     Long userId = addUser(newUser);  
     User user = getUser(userId);  
     displayUser(user);  
     user.setFirstName("Updated " + user.getFirstName());  
     userId = updateUser(user);  
     user = getUser(userId);  
     displayUser(user);  
     addUser( new User("AnotherFirstName", "AnotherLastName"));  
     getAllUsers();  
   }  
   private static Long addUser(User newUser) throws Exception {  
     String requestJson = "{\"user\":" + JSON_CONVERTER.toJson(newUser) + "}";  
     displayRequest(requestJson);  
     ClientRequest request = createRequest(SERVICE_URL_ADD).body(CONTENT_TYPE, requestJson);  
     ClientResponse<String> response = request.post(String.class);  
     displayResponse(response, HTTP_CODE_CREATED);  
     return Long.parseLong(response.getEntity().toString());  
   }  
   private static Long updateUser(User user) throws Exception {  
     String requestJson = "{\"user\":" + JSON_CONVERTER.toJson(user) + "}";  
     displayRequest(requestJson);  
     ClientRequest request = createRequest(SERVICE_URL_UPDATED).body(CONTENT_TYPE, requestJson);  
     ClientResponse<String> response = request.post(String.class);  
     displayResponse(response, HTTP_CODE_OK);  
     return Long.parseLong(response.getEntity().toString());  
   }  
   private static User getUser(Long userId) throws Exception {  
     ClientRequest request = createRequest(SERVICE_URL_GET.replace("{id}", userId.toString()));  
     ClientResponse<String> response = request.get(String.class);  
     displayResponse(response, HTTP_CODE_OK);  
     String responseJson = response.getEntity().toString();  
     responseJson = responseJson.replace("{\"user\":", "").replace("}}", "}");  
     return JSON_CONVERTER.fromJson(responseJson, User.class);  
   }  
   private static void getAllUsers() throws Exception {  
     ClientRequest request = createRequest(SERVICE_URL_GET_ALL);  
     ClientResponse<String> response = request.get(String.class);  
     displayResponse(response, HTTP_CODE_OK);  
   }  
   private static ClientRequest createRequest(String uri) {  
     ClientRequest request = new ClientRequest(SERVICE_URL + uri);  
     request.accept("application/json");  
     return request;  
   }  
   private static void displayUser(User user) {  
     System.out.println(user);  
   }  
   private static void displayRequest(String request) {  
     System.out.println(request);  
   }  
   private static void displayResponse(ClientResponse<String> response, int expectedResponseCode) throws IOException {  
     if (response.getStatus() != expectedResponseCode) {  
       throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());  
     }  
     BufferedReader br = new BufferedReader(new InputStreamReader(  
         new ByteArrayInputStream(response.getEntity().getBytes())));  
     String output;  
     System.out.println("Output from Server .... \n");  
     while ((output = br.readLine()) != null) {  
       System.out.println(output);  
     }  
   }  
 }  

Summary:
This tutorial looked at how we can consume RESTful Web Service using RESTEasy.

You can find the sample code here.

RESTful with RESTEasy - Part 1/4 (Creating a POST and GET RESTful Service)

Overview:
This post looks at RESTEasy framework, which is an excellent framework for developing RESTful services, and has gained a lot of popularity in the Mobile application development world.


Also Refer to,
RESTful with RESTEasy - Part 2/4 (Client Consuming a POST and GET RESTful Service)


You can find the sample code here.

Prerequisite:JDK >= 1.5.x
Maven >= 2.2.x

Project Structure: Following is the project structure I have used (Maven WAR project).

mvn archetype:generate -DgroupId=org.fazlan.resteasy.spring.servic -DartifactId=org.fazlan.resteasy.spring.servic -DarchetypeArtifactId=maven-archetype-webapp -Dversion=1.0.0-SNAPSHOT -DinteractiveMode=false



Step 1: Maven Dependencies (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/maven-v4_0_0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan</groupId>  
   <artifactId>org.fazlan.resteasy.spring.service</artifactId>  
   <packaging>war</packaging>  
   <version>1.0.0</version>  
   <name>org.fazlan.resteasy.spring.service Maven Webapp</name>  
   <url>http://maven.apache.org</url>  
   <properties>  
     <resteasy.version>2.3.4.Final</resteasy.version>  
     <org.springframework.version>3.1.2.RELEASE</org.springframework.version>  
     <slf4j.version>1.6.1</slf4j.version>  
   </properties>  
   <dependencies>  
     <!-- Spring 3 dependencies -->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-core</artifactId>  
       <version>3.1.2.RELEASE</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-web</artifactId>  
       <version>3.1.2.RELEASE</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-webmvc</artifactId>  
       <version>3.1.2.RELEASE</version>  
     </dependency>  
     <!-- jstl -->  
     <dependency>  
       <groupId>javax.servlet</groupId>  
       <artifactId>jstl</artifactId>  
       <version>1.2</version>  
     </dependency>  
     <dependency>  
       <groupId>org.jboss.resteasy</groupId>  
       <artifactId>resteasy-jaxrs</artifactId>  
       <version>${resteasy.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.jboss.resteasy</groupId>  
       <artifactId>resteasy-jaxb-provider</artifactId>  
       <version>${resteasy.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.jboss.resteasy</groupId>  
       <artifactId>resteasy-spring</artifactId>  
       <version>${resteasy.version}</version>  
       <exclusions>  
         <exclusion>  
           <artifactId>commons-logging</artifactId>  
           <groupId>commons-logging</groupId>  
         </exclusion>  
         <exclusion>  
           <artifactId>jaxb-impl</artifactId>  
           <groupId>com.sun.xml.bind</groupId>  
         </exclusion>  
         <exclusion>  
           <artifactId>sjsxp</artifactId>  
           <groupId>com.sun.xml.stream</groupId>  
         </exclusion>  
         <exclusion>  
           <artifactId>jsr250-api</artifactId>  
           <groupId>javax.annotation</groupId>  
         </exclusion>  
         <exclusion>  
           <artifactId>activation</artifactId>  
           <groupId>javax.activation</groupId>  
         </exclusion>  
       </exclusions>  
     </dependency>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>3.8.1</version>  
       <scope>test</scope>  
     </dependency>  
     <dependency>  
       <groupId>com.google.code.gson</groupId>  
       <artifactId>gson</artifactId>  
       <version>1.7.1</version>  
     </dependency>  
   </dependencies>  
   <build>  
     <finalName>UserService</finalName>  
     <plugins>  
       <plugin>  
         <groupId>org.mortbay.jetty</groupId>  
         <artifactId>maven-jetty-plugin</artifactId>  
         <version>6.1.10</version>  
         <configuration>  
           <contextPath>UserService</contextPath>  
           <scanIntervalSeconds>30</scanIntervalSeconds>  
           <stopKey>foo</stopKey>  
           <stopPort>9999</stopPort>  
         </configuration>  
       </plugin>  
     </plugins>  
   </build>  
 </project>  

Step 2: Creating POJO Classes and Service Implementation

User.java

 package org.fazlan.resteasy.spring.entity; 
 
 import javax.xml.bind.annotation.XmlRootElement;  

 @XmlRootElement  
 public class User {  

   private long id;  
   private String firstName;  
   private String lastName;  

   public User() {  
   }  

   public User(String firstName, String lastName) {  
     this.firstName = firstName;  
     this.lastName = lastName;  
   }  

   public long getId() {  
     return id;  
   }  

   @Override  
   public String toString() {  
     return "User{" +  
         "id=" + id +  
         ", firstName='" + firstName + '\'' +  
         ", lastName='" + lastName + '\'' +  
         '}';  
   }  

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

   public String getFirstName() {  
     return firstName;  
   }  

   public void setFirstName(String firstName) {  
     this.firstName = firstName;  
   }  

   public String getLastName() {  
     return lastName;  
   }  

   public void setLastName(String lastName) {  
     this.lastName = lastName;  
   }  
 }  


UserService.java - The Service Interface
 package org.fazlan.resteasy.spring.service;  
 import org.fazlan.resteasy.spring.entity.User;  
 import javax.ws.rs.*;  
 import javax.ws.rs.core.MediaType;  
 import javax.ws.rs.core.Response;  

 @Path("/")  
 public interface UserService {  

   @POST  
   @Path("/add")  
   @Consumes(MediaType.APPLICATION_JSON)  
   Response add(User user);  

   @POST  
   @Path("/update")  
   @Consumes(MediaType.APPLICATION_JSON)  
   Response update(User user);  

   @GET  
   @Path("/get/{id}")  
   @Produces(MediaType.APPLICATION_JSON)  
   Response get(@PathParam("id") long id);  

   @GET  
   @Path("/getAll")  
   @Produces(MediaType.APPLICATION_JSON)  
   Response getAll();  
 }  

NOTE: This service class has been annotated to produce and consume certain methods in JSON(application/json) as the mime-type.

UserServiceImpl.java - Service Implementation
 package org.fazlan.resteasy.spring.service;  
 
 import org.fazlan.resteasy.spring.entity.User;  
 import org.springframework.stereotype.Component;  
 import javax.ws.rs.PathParam;  
 import javax.ws.rs.core.GenericEntity;  
 import javax.ws.rs.core.Response;  
 import java.util.Collection;  
 import java.util.HashMap;  
 import java.util.Map;  

 @Component  
 public class UserServiceImpl implements UserService {  

   private static long ID = 0L;  
   private static Map<Long, User> users = new HashMap<Long, User>();  

   public Response add(User user) {  
     user.setId(++ID);  
     update(user);  
     return Response.status(Response.Status.CREATED).entity(ID).build();  
   }  

   public Response update(User user) {  
     users.put(ID, user);  
     return Response.status(Response.Status.OK).entity(ID).build();  
   }  

   public Response get(@PathParam("id") long id) {  
     return Response.status(Response.Status.OK).entity(users.get(id)).build();  
   }  

   public Response getAll() {  
     GenericEntity<Collection<User>> usersEntity = new GenericEntity<Collection<User>>(users.values()) {  
     };  
     return Response.status(Response.Status.OK).entity(usersEntity).build();  
   }  
 }  

Step 3: Exposing the Service Beans as RESTful Web Service (applicationContext.xml)

 <?xml version="1.0" encoding="UTF-8"?>  
 <beans xmlns="http://www.springframework.org/schema/beans"  
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
     xmlns:context="http://www.springframework.org/schema/context"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
       http://www.springframework.org/schema/context  
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
   <context:annotation-config/>  
   <context:component-scan base-package="org.fazlan.resteasy.spring.service"/>  
 </beans>  

NOTE: This will scan all the Spring components defined in the source and load them into the Application's Spring context.

Step 4: Updating the web.xml to register the RESTEasy Servlet.
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xmlns="http://java.sun.com/xml/ns/javaee"  
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
      id="WebApp_ID" version="3.0">  
   <display-name>Restful Web Application</display-name>  
   <!--<context-param>-->  
   <!--<param-name>resteasy.scan</param-name>-->  
   <!--<param-value>true</param-value>-->  
   <!--</context-param>-->  
   <context-param>  
     <param-name>contextConfigLocation</param-name>  
     <param-value>classpath:applicationContext.xml</param-value>  
   </context-param>  
   <listener>  
     <listener-class>  
       org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap  
     </listener-class>  
   </listener>  
   <listener>  
     <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>  
   </listener>  
   <servlet>  
     <servlet-name>resteasy-servlet</servlet-name>  
     <servlet-class>  
       org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher  
     </servlet-class>  
   </servlet>  
   <servlet-mapping>  
     <servlet-name>resteasy-servlet</servlet-name>  
     <url-pattern>/*</url-pattern>  
   </servlet-mapping>  
 </web-app>  


Step 5: Building the Project
mvn clean install

Step 6: Deploying the Service
Now you can deploy this service on to any container. Here we'll deploy this on Jetty using Maven.

mvn jetty:run

This should start a Jetty instance and deploy the application. You can access the HTTP GET service endpoints at the following endpoints.

http://localhost:8080/UserService/get/1 - Get the user with Id = 1
http://localhost:8080/UserService/getAll - List all users

Step 7: Testing the RESTful Web Service using CURL


We're going to test our Web Service using CURL, which I found as a very easy way to send REST Calls. I found this very nice blog about RESTing with Curl.

Installing CURL

sudo apt-get install curl

-I am using Ubuntu, if you're using Win or Mac, please do the necessary to install curl.

Now, after successfully installing CURL, we are ready to test the services.

GET Resources:
- Retrieving all the users.
 curl -v -H "Accept: application/json" http://localhost:8080/UserService/getAll

- Retrieving a user for a given id(id = 1).
 curl -v -H "Accept: application/json" http://localhost:8080/UserService/get/1

POST a Resource: Adding a new user resource.
- Adding the new user record in the add.json file.
 { 
  "user":{ 
    "id":1, 
    "firstName":"First Name1", 
    "lastName":"Last Name1" 
  } 
} 

 curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d @add.json http://127.0.0.1:8080/UserService/add


PUT a Resource: Updating an existing user resource.
- Updating an existing user record in the update.json file.
 { 
  "user":{ 
    "id":1, 
    "firstName":"Updated First Name1", 
    "lastName" :"Updated Last Name1" 
  } 
} 

 curl -v -H "Accept: application/json" -H "Content-type: application/json" -X PUT -d @update.json http://127.0.0.1:8080/UserService/update/

Summary:
This tutorial looked at how we can create RESTful Web Service using RESTEasy and test the services using CURL.

You can find the sample code here.

Sunday, June 17, 2012

How to Enable UTF-8 Support on Tomcat

Overview:
This is a brief article on enabling the support for displaying UTF-8 characters (e.i: Japanese font) on JSP/HTML pages of a web applications on Tomcat. We can achieve this with THREE easy steps.

To make UTF-8 working under Java, Tomcat, Linux/Windows, it requires the following:
  1. Update Tomcat's server.xml
  2. Define a javax.servlet.Filter and Update the Web Application's web.xml
  3. Enable UTF-8 encoding on JSP/HTML
Update Tomcat's server.xml
This handles GET request URL. With this configuration, the Connector uses UTF-8 encoding to handle all incoming GET request parameters.

 <Connector   
         . . .  
         URIEncoding="UTF-8"/>  

 http://localhost:8080/foo-app/get?foo_param=こんにちは世界  

e.i: request.getParameter("foo_param") // the value retrieved will be encoded with UTF-8 and you'll get the UTF-8 value as it is("こんにちは世界").

IMPORTANT NOTE: POST requests will have NO effect by this change.

Define a javax.servlet.Filter and Update the Web Application's web.xml
Now, we need to enforce our web application to handle all requests and response in terms of UTF-8 encoding. This way, we are handling POST requests as well. For this purpose, we need to define a character set filter that'll transform all the requests and response into UTF-8 encoding in the following manner.

 package org.fazlan.tomcat.ext.filter;
  
 import javax.servlet.Filter;  
 import javax.servlet.FilterChain;  
 import javax.servlet.FilterConfig;  
 import javax.servlet.ServletException;  
 import javax.servlet.ServletRequest;  
 import javax.servlet.ServletResponse;  
 import java.io.IOException;  

 /***  
  * This is a filter class to force the java webapp to handle all requests and responses as UTF-8 encoded by default.  
  * This requires that we define a character set filter.  
  * This filter makes sure that if the browser hasn't set the encoding used in the request, that it's set to UTF-8.  
  */  
 public class CharacterSetFilter implements Filter {  

   private static final String UTF8 = "UTF-8";  
   private static final String CONTENT_TYPE = "text/html; charset=UTF-8";  
   private String encoding;  

   @Override  
   public void init(FilterConfig config) throws ServletException {  
     encoding = config.getInitParameter("requestCharEncoding");  
     if (encoding == null) {  
       encoding = UTF8;  
     }  
   }  

   @Override  
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
     // Honour the client-specified character encoding  
     if (null == request.getCharacterEncoding()) {  
       request.setCharacterEncoding(encoding);  
     }  
     /**  
      * Set the default response content type and encoding  
      */  
     response.setContentType(CONTENT_TYPE);  
     response.setCharacterEncoding(UTF8);  
     chain.doFilter(request, response);  
   }  

   @Override  
   public void destroy() {  
   }  
 }  

The filter ensures that if the browser has not set the encoding format in the request, UTF-8 is set as the default encoding. Also, it sets UTF-8 as the default response encoding.

Now, we need to add this to our web application's web.xml to make it work.
 . . .
 <filter>  
   <filter-name>CharacterSetFilter</filter-name>  
   <filter-class>org.fazlan.tomcat.ext.filter.CharacterSetFilter</filter-class>  
   <init-param>  
     <param-name>requestEncoding</param-name>  
     <param-value>UTF-8</param-value>  
   </init-param>  
 </filter>  
 <filter-mapping>  
   <filter-name>CharacterSetFilter</filter-name>  
   <url-pattern>/*</url-pattern>  
 </filter-mapping>  
 . . .

Enable UTF-8 encoding on JSP/HTML
JSP Pages
All JSP pages that needs to render UTF-8 content needs to have the following on top the page declaration.

 <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>  

HTML Pages 
All HTML pages that needs to render UTF-8 content needs to have the following in their header section.

 <?xml version="1.0" encoding="UTF-8"?>  
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">  
 <head>  
 <meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />  
 ...  
 </head>  

Summary: 
The article looked at how to support UTF-8 content in your web application deployed on Tomcat.

Thursday, June 7, 2012

Apache Lucene Tutorial: Indexing PDF Files

Overview:
This article is a sequel to Apache Lucene Tutorial: Lucene for Text Search. Here, we look at how to index content in a PDF file. Apache Lucene doesn't have the build-in capability to process PDF files. Therefore, we need to use one of the APIs that enables us to perform text manipulation on PDF files.

One such library is Apache PDFBox, which we'll use in the article. You can read more about Apache PDFBox.

Article applies to Lucene 3.6.0 and PDFBox 0.7.3.

Sample code can be found here.

You may also refer to Apache Lucene Tutorial: Indexing Microsoft Documents

Project Structure:

 org.fazlan.lucene.pdf.demo  
 |-- pom.xml  
 `-- src  
   `-- main  
     |-- java  
     |  `-- org  
     |    `-- fazlan  
     |      `-- lucene  
     |        `-- demo  
     |          |-- FileIndexApplication.java  
     |          |-- FileIndexer.java  
     |          |-- Indexer.java  
     |          |-- IndexItem.java  
     |          |-- PDFIndexer.java  
     |          `-- Searcher.java  
     `-- resources  
       `-- files  
         `-- HelloPDFBox.pdf  

Step 1: Creating the Project

 mvn archetype:generate -DartifactId=org.fazlan.lucene.pdf.demo -DgroupId=org.fazlan -Dversion=1.0-SNAPSHOT -DinteractiveMode=false  

Step 2: Updating the Maven Dependencies

 <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/maven-v4_0_0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>org.fazlan</groupId>  
  <artifactId>org.fazlan.lucene.pdf.demo</artifactId>  
  <packaging>jar</packaging>  
  <version>1.0-SNAPSHOT</version>  
  <name>org.fazlan.lucene.demo</name>  
  <url>http://maven.apache.org</url>  
  <dependencies>  
   <dependency>  
      <groupId>org.apache.lucene</groupId>  
      <artifactId>lucene-core</artifactId>  
      <version>3.6.0</version>  
   </dependency>  
    <dependency>  
      <groupId>pdfbox</groupId>  
      <artifactId>pdfbox</artifactId>  
      <version>0.7.3</version>  
    </dependency>  
   <dependency>  
    <groupId>junit</groupId>  
    <artifactId>junit</artifactId>  
    <version>3.8.1</version>  
    <scope>test</scope>  
   </dependency>  
  </dependencies>  
 </project>  

Step 3: Defining the PDF Indexer


This is the most important component. The following code will load the content from a PDF file, and the extracted content is form into a String representation so that it can be further processed by Lucene for indexing purposes.

 package org.fazlan.lucene.demo;  
 import org.pdfbox.pdmodel.PDDocument;  
 import org.pdfbox.util.PDFTextStripper;  
 import java.io.File;  
 import java.io.IOException;  
 public class PDFIndexer implements FileIndexer {  
   public IndexItem index(File file) throws IOException {  
     PDDocument doc = PDDocument.load(file);  
     String content = new PDFTextStripper().getText(doc);  
     doc.close();  
     return new IndexItem((long)file.getName().hashCode(), file.getName(), content);  
   }  
 }  

Step 4: Writing a Application


The following is a sample application code to index a PDF file.

 package org.fazlan.lucene.demo;  

 import org.apache.lucene.queryParser.ParseException;  
 import java.io.File;  
 import java.io.IOException;  
 import java.util.List;  

 public class FileIndexApplication {  

   // location where the index will be stored.  
   private static final String INDEX_DIR = "src/main/resources/index";  
   private static final int DEFAULT_RESULT_SIZE = 100;  

   public static void main(String[] args) throws IOException, ParseException {  

     File pdfFile = new File("src/main/resources/files/HelloPDFBox.pdf");  
     IndexItem pdfIndexItem = new PDFIndexer().index(pdfFile);  
     // creating the indexer and indexing the items  

     Indexer indexer = new Indexer(INDEX_DIR);  
     indexer.index(pdfIndexItem);  

     // close the index to enable them index  
     indexer.close();  

     // creating the Searcher to the same index location as the Indexer  
     Searcher searcher = new Searcher(INDEX_DIR);  
     List<IndexItem> result = searcher.findByContent("World", DEFAULT_RESULT_SIZE);  
     print(result);  

     searcher.close();  
   }  
    /**  
    * print the results.  
    */  
   private static void print(List<IndexItem> result) {  
     System.out.println("Result Size: " + result.size());  
     for (IndexItem item : result) {  
       System.out.println(item);  
     }  
   }  
 }  

Summary:
This was a brief article on how to integrate Apache PDFBox with Apache Lucene for indexing the contents in a PDF file.

Sample code can be found here.

Apache Lucene Tutorial: Indexing Microsoft Documents

Overview:
This article is a sequel to Apache Lucene Tutorial: Lucene for Text Search. Here, we look at how to index content in a Microsoft documents such as Word, Excel and PowerPoint files. Apache Lucene doesn't have the build-in capability to process these files. Therefore, we need to use one of the APIs that enables us to perform text manipulation on MS documents files.

One such library is Apache POI, which we'll use in the article. You can read more about Apache POI.

Article applies to Lucene 3.6.0 and POI 3.8.0.

Sample code can be found here.

You may also refer to Apache Lucene Tutorial: Indexing PDF Files

Project Structure:

 org.fazlan.lucene.ms.demo  
 |-- pom.xml  
 `-- src  
   `-- main  
     |-- java  
     |  `-- org  
     |    `-- fazlan  
     |      `-- lucene  
     |        `-- demo  
     |          |-- FileIndexApplication.java  
     |          |-- FileIndexer.java  
     |          |-- Indexer.java  
     |          |-- IndexItem.java  
     |          |-- MSDocumentIndexer.java  
     |          `-- Searcher.java  
     `-- resources  
       `-- files  
         |-- MSExcell.xls  
         |-- MSWord.doc  
         `-- MSWord.docx  

Step 1: Creating the Project

 mvn archetype:generate -DartifactId=org.fazlan.lucene.ms.demo -DgroupId=org.fazlan -Dversion=1.0-SNAPSHOT -DinteractiveMode=false  

Step 2: Updating the Maven Dependencies

 <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/maven-v4_0_0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan</groupId>  
   <artifactId>org.fazlan.lucene.ms.demo</artifactId>  
   <packaging>jar</packaging>  
   <version>1.0-SNAPSHOT</version>  
   <name>org.fazlan.lucene.demo</name>  
   <url>http://maven.apache.org</url>  
   <dependencies>  
     <dependency>  
       <groupId>org.apache.lucene</groupId>  
       <artifactId>lucene-core</artifactId>  
       <version>3.6.0</version>  
     </dependency>  
     <dependency>  
       <groupId>org.apache.poi</groupId>  
       <artifactId>poi</artifactId>  
       <version>3.8</version>  
     </dependency>  
     <dependency>  
       <groupId>org.apache.poi</groupId>  
       <artifactId>poi-ooxml</artifactId>  
       <version>3.8</version>  
     </dependency>  
     <dependency>  
       <groupId>org.apache.poi</groupId>  
       <artifactId>poi-scratchpad</artifactId>  
       <version>3.8</version>  
     </dependency>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>3.8.1</version>  
       <scope>test</scope>  
     </dependency>  
   </dependencies>  
 </project>  

Step 3: Defining the MS Document Indexer


This is the most important component. The following code will load the content from a MS Word, MS Excel, MS PowerPoint or Visio file, and the extracted content is form into a String representation so that it can be further processed by Lucene for indexing purposes.

Extractors already exist for Excel, Word, PowerPoint and Visio; if one of these objects is embedded into a worksheet, the ExtractorFactory class can be used to recover an extractor for it based on the file extension.

 package org.fazlan.lucene.demo;  

 import org.apache.poi.extractor.ExtractorFactory;  
 import java.io.File;  
 import java.io.IOException;  

 public class MSDocumentIndexer implements FileIndexer {  

   public IndexItem index(File file) throws IOException {  

     String content = "";  
     try {  
       content = ExtractorFactory.createExtractor(file).getText();  
     } catch (Exception e) {  
       e.printStackTrace();  
     }  

     return new IndexItem((long) file.hashCode(), file.getName(), content);  
   }  
 }  

The above code uses a new class in POI 3.5 or higher, org.apache.poi.extractor.ExtractorFactory provides a similar function to WorkbookFactory. You simply pass it an InputStream, a file, a POIFSFileSystem or a OOXML Package. It figures out the correct text extractor for you, and returns it.

Using POI < 3.5 to Extracting Text Content

Extracting Content from MS Word Document
 //MS Word  
 POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream("MS Word.doc"));   
 WordExtractor extractor = new WordExtractor(fs);   
 String content = extractor.getText();  


Extracting Content from MS Excel Document
 //MS Excel  
 POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream("MS Excel.xls"));   
 ExcelExtractor extractor = new ExcelExtractor(fs);   
 String content = extractor.getText();   

Extracting Content from MS PowerPoint Document
 /MS PowerPoint  
 POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream("MS PowerPoint.ppt"));   
 PowerPointExtractor extractor = new PowerPointExtractor(fs);   
 String content = extractor.getText();   

Step 4: Writing a Application


The following is a sample application code to index a MS document file.

 package org.fazlan.lucene.demo;  

 import org.apache.lucene.queryParser.ParseException;  
 import java.io.File;  
 import java.io.IOException;  
 import java.util.List;  

 public class FileIndexApplication {  

   // location where the index will be stored.  
   private static final String INDEX_DIR = "src/main/resources/index";  
   private static final int DEFAULT_RESULT_SIZE = 100;  

   public static void main(String[] args) throws IOException, ParseException {  

     MSDocumentIndexer msDocumentIndexer = new MSDocumentIndexer();  
     File msWordFile = new File("src/main/resources/files/MSWord.doc");  
     File msWord2003File = new File("src/main/resources/files/MSWord.docx");  
     File msExcellFile = new File("src/main/resources/files/MSExcell.xls");  

     // creating the indexer and indexing the items  
     Indexer indexer = new Indexer(INDEX_DIR);  
     indexer.index(msDocumentIndexer.index(msWordFile));  
     indexer.index(msDocumentIndexer.index(msWord2003File));  
     indexer.index(msDocumentIndexer.index(msExcellFile));  

     // close the index to enable them index  
     indexer.close();  

     // creating the Searcher to the same index location as the Indexer  
     Searcher searcher = new Searcher(INDEX_DIR);  
     List<IndexItem> result = searcher.findByContent("Microfost", DEFAULT_RESULT_SIZE);  
     print(result);  

     searcher.close();  
   }  

    /**  
    * print the results.  
    */  
   private static void print(List<IndexItem> result) {  
     System.out.println("Result Size: " + result.size());  
     for (IndexItem item : result) {  
       System.out.println(item);  
     }  
   }  
 }  

Summary:
This was a brief article on how to integrate Apache POI with Apache Lucene for indexing the contents in a MS documents such as Word, Excel and PowerPoint file.

Sample code can be found here.

Monday, June 4, 2012

Apache Lucene Tutorial: Lucene for Text Search

Overview:
This article looks at how Apache Lucene can be used to perform text based searching. Lucene provides a high-performance text based search capabilities. This is a very easy to use library.

This article applies to Lucene 3.6 (latest release at the time of writing).

The sample code can be found here.

You may also refer to,
Apache Lucene Tutorial: Indexing PDF Files
Apache Lucene Tutorial: Indexing Microsoft Documents

Project Structure:

 org.fazlan.lucene.demo  
 |-- pom.xml  
 `-- src  
   `-- main  
     |-- java  
     |  `-- org  
     |    `-- fazlan  
     |      `-- lucene  
     |        `-- demo  
     |          |-- Indexer.java  
     |          |-- IndexItem.java  
     |          |-- Main.java  
     |          `-- Searcher.java  
     `-- resources  
       `-- index  

Step 1: Creating a Maven Project

 mvn archetype:generate -DartifactId=org.fazlan.lucene.demo -DgroupId=org.fazlan -Dversion=1.0-SNAPSHOT -DinteractiveMode=false  

Step 2: Updated Maven Dependency (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/maven-v4_0_0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>org.fazlan</groupId>  
  <artifactId>org.fazlan.lucene.demo</artifactId>  
  <packaging>jar</packaging>  
  <version>1.0-SNAPSHOT</version>  
  <name>org.fazlan.lucene.demo</name>  
  <url>http://maven.apache.org</url>  
  <dependencies>  
   <dependency>  
      <groupId>org.apache.lucene</groupId>  
      <artifactId>lucene-core</artifactId>  
      <version>3.6.0</version>  
   </dependency>  
   <dependency>  
    <groupId>junit</groupId>  
    <artifactId>junit</artifactId>  
    <version>3.8.1</version>  
    <scope>test</scope>  
   </dependency>  
  </dependencies>  
 </project>  

Step 3: Defining the POJO Class used to Index Items


 package org.fazlan.lucene.demo;  
 public class IndexItem {  
   private Long id;  
   private String title;  
   private String content;  
   public static final String ID = "id";  
   public static final String TITLE = "title";  
   public static final String CONTENT = "content";  
   public IndexItem(Long id, String title, String content) {  
     this.id = id;  
     this.title = title;  
     this.content = content;  
   }  
   public Long getId() {  
     return id;  
   }  
   public String getTitle() {  
     return title;  
   }  
   public String getContent() {  
     return content;  
   }  
   @Override  
   public String toString() {  
     return "IndexItem{" +  
         "id=" + id +  
         ", title='" + title + '\'' +  
         ", content='" + content + '\'' +  
         '}';  
   }  
 }  

Step 4: Defining the Indexer

 package org.fazlan.lucene.demo;  
 import org.apache.lucene.analysis.standard.StandardAnalyzer;  
 import org.apache.lucene.document.Document;  
 import org.apache.lucene.document.Field;  
 import org.apache.lucene.index.IndexWriter;  
 import org.apache.lucene.index.IndexWriterConfig;  
 import org.apache.lucene.index.Term;  
 import org.apache.lucene.store.FSDirectory;  
 import org.apache.lucene.util.Version;  
 import java.io.File;  
 import java.io.IOException;  
 public class Indexer  
 {  
   private IndexWriter writer;  
   public Indexer(String indexDir) throws IOException {  
     // create the index  
     if(writer == null) {  
         writer = new IndexWriter(FSDirectory.open(  
           new File(indexDir)), new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36)));  
     }  
   }  
   /**   
    * This method will add the items into index  
    */  
   public void index(IndexItem indexItem) throws IOException {  
     // deleting the item, if already exists  
     writer.deleteDocuments(new Term(IndexItem.ID, indexItem.getId().toString()));  
     Document doc = new Document();  
     doc.add(new Field(IndexItem.ID, indexItem.getId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));  
     doc.add(new Field(IndexItem.TITLE, indexItem.getTitle(), Field.Store.YES, Field.Index.ANALYZED));  
     doc.add(new Field(IndexItem.CONTENT, indexItem.getContent(), Field.Store.YES, Field.Index.ANALYZED));  
     // add the document to the index  
     writer.addDocument(doc);  
   }  
   /**  
    * Closing the index  
    */  
   public void close() throws IOException {  
     writer.close();  
   }  
 }  

Field.Store.YES: if you need to store the value, so that the value can be retrieved from the searched result.
Field.Index.ANALYZED: Index the tokens produced by running the field's value through an Analyzer.

Field.Index.NOT_ANALYZED: Index the field's value without using an Analyzer, so it can be searched.

Step 5: Defining the Searcher

 package org.fazlan.lucene.demo;  
 import org.apache.lucene.analysis.standard.StandardAnalyzer;  
 import org.apache.lucene.document.Document;  
 import org.apache.lucene.index.IndexReader;  
 import org.apache.lucene.queryParser.ParseException;  
 import org.apache.lucene.queryParser.QueryParser;  
 import org.apache.lucene.search.*;  
 import org.apache.lucene.store.FSDirectory;  
 import org.apache.lucene.util.Version;  
 import java.io.File;  
 import java.io.IOException;  
 import java.util.ArrayList;  
 import java.util.List;  

 public class Searcher {  

   private IndexSearcher searcher;  
   private QueryParser titleQueryParser;  
   private QueryParser contentQueryParser;  

   public Searcher(String indexDir) throws IOException {  
     // open the index directory to search  
     searcher = new IndexSearcher(IndexReader.open(FSDirectory.open(new File(indexDir))));  
     StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);  
     // defining the query parser to search items by title field.  
     titleQueryParser = new QueryParser(Version.LUCENE_36, IndexItem.TITLE, analyzer);  
     // defining the query parser to search items by content field.  
     contentQueryParser = new QueryParser(Version.LUCENE_36, IndexItem.CONTENT, analyzer);  
   }  

   /**  
    * This method is used to find the indexed items by the title.  
    * @param queryString - the query string to search for  
    */  
   public List<IndexItem> findByTitle(String queryString, int numOfResults) throws ParseException, IOException {  
     // create query from the incoming query string.  
     Query query = titleQueryParser.parse(queryString);  
     // execute the query and get the results  
     ScoreDoc[] queryResults = searcher.search(query, numOfResults).scoreDocs;  
     List<IndexItem> results = new ArrayList<IndexItem>();  
     // process the results  
     for (ScoreDoc scoreDoc : queryResults) {  
       Document doc = searcher.doc(scoreDoc.doc);  
       results.add(new IndexItem(Long.parseLong(doc.get(IndexItem.ID)), doc.get(IndexItem.TITLE), doc.get(IndexItem  
           .CONTENT)));  
     }  
      return results;  
   }  

   /**  
    * This method is used to find the indexed items by the content.  
    * @param queryString - the query string to search for  
    */  
   public List<IndexItem> findByContent(String queryString, int numOfResults) throws ParseException, IOException {  
     // create query from the incoming query string.  
     Query query = contentQueryParser.parse(queryString);  
     // execute the query and get the results  
     ScoreDoc[] queryResults = searcher.search(query, numOfResults).scoreDocs;  
     List<IndexItem> results = new ArrayList<IndexItem>();  
     // process the results  
     for (ScoreDoc scoreDoc : queryResults) {  
       Document doc = searcher.doc(scoreDoc.doc);  
       results.add(new IndexItem(Long.parseLong(doc.get(IndexItem.ID)), doc.get(IndexItem.TITLE), doc.get(IndexItem  
           .CONTENT)));  
     }  
      return results;  
   }  

   public void close() throws IOException {  
     searcher.close();  
   }  
 }  

Step 6: The Application using the Indexer and the Searcher

 package org.fazlan.lucene.demo; 
 
 import org.apache.lucene.queryParser.ParseException;  
 import java.io.BufferedReader;  
 import java.io.IOException;  
 import java.io.InputStreamReader;  
 import java.util.List;  

 public class Main {  
   // location where the index will be stored.  
   private static final String INDEX_DIR = "src/main/resources/index";  
   private static final int DEFAULT_RESULT_SIZE = 100;  

   public static void main(String[] args) throws IOException, ParseException {  
     // the items to be indexed  
     IndexItem[] indexItems = {  
         new IndexItem(1L, "Java in Action", "This is Java in Action Book"),  
         new IndexItem(2L, "Spring in Action", "This is Spring in Action Book"),  
         new IndexItem(3L, "Hibernate in Action", "This is Hibernate in Action Book"),  
         new IndexItem(4L, "SOA in Action", "This is SOA in Action Book"),  
         new IndexItem(5L, "Apache Axis2 in Action", "This is Axis2 in Action Book"),  
         new IndexItem(6L, "Apache CXF in Action", "This is CXF in Action Book"),  
         new IndexItem(7L, "jQuery in Action", "This is jQuery in Action Book")};  

     // creating the indexer and indexing the items  
     Indexer indexer = new Indexer(INDEX_DIR);  
     for (IndexItem indexItem : indexItems) {  
       indexer.index(indexItem);  
     }  

     // close the index to enable them index  
     indexer.close();  

     BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));  
     String input;  
     System.out.println("Type Q/q to quit.");  
     System.out.println("Type 1 query by title.");  
     System.out.println("Type 2 query by content.");  

     // creating the Searcher to the same index location as the Indexer  
     Searcher searcher = new Searcher(INDEX_DIR);  

     do {  
       System.out.print("Enter input: ");  
       input = reader.readLine();  
       if (input.equalsIgnoreCase("q")) {  
         break;  
       }  

       // search by title  
       if (input.equals("1")) {  
         System.out.print("Enter title to search: ");  
         input = reader.readLine();  
         List<IndexItem> result = searcher.findByTitle(input, DEFAULT_RESULT_SIZE);  
         print(result);  

       } else if (input.equals("2")) { // else, search by content  
         System.out.print("Enter content to search: ");  
         input = reader.readLine();  
         List<IndexItem> result = searcher.findByContent(input, DEFAULT_RESULT_SIZE);  
         print(result);  
       }  
     } while (true);  

     searcher.close();  
   }  

   /**  
    * print the results.  
    */  
   private static void print(List<IndexItem> result) {  
     System.out.println("Result Size: " + result.size());  
     for (IndexItem item : result) {  
       System.out.println(item);  
     }  
   }  
 }  

Summary:
This article looked at how you can easily introduce text based indexing into your application using Apache Lucene.

The sample code can be found here.

Monday, May 28, 2012

jQuery Tutorial: Integrating with Flickr's JSON API

Overview:
This will be another article on jQuery. Here, we'll be mainly focusing on how jQuery eases the usage of the Ajax API for JSON.

You may also refer to the first jQuery article "jQuery Tutorial: Ajax Processing - GET and POST".

The sample code for this article can be found here.

Application Demo:
The application displays images from Flickr.com that are tagged by the input provided by the user.

Home page:


Displaying the Result:



Project Structure:

 org.fazlan.jquery.flickr.app  
 |-- pom.xml  
 `-- src  
   `-- main  
     |-- resources  
     `-- webapp  
       |-- img  
       |  `-- ajax-loader.gif  
       |-- index.jsp  
       |-- jquery.js  
       `-- WEB-INF  
         `-- web.xml  

Step 1: Creating a the Web Project.

 <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/maven-v4_0_0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan</groupId>  
   <artifactId>org.fazlan.jquery.flickr.app</artifactId>  
   <packaging>war</packaging>  
   <version>1.0.0-SNAPSHOT</version>  
   <name>org.fazlan.jquery.flickr.app Maven Webapp</name>  
   <url>http://maven.apache.org</url>  
   <dependencies>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>3.8.1</version>  
       <scope>test</scope>  
     </dependency>  
   </dependencies>  
   <build>  
     <finalName>jquery.flickr.app</finalName>  
     <plugins>  
       <plugin>  
         <groupId>org.mortbay.jetty</groupId>  
         <artifactId>maven-jetty-plugin</artifactId>  
         <version>6.1.10</version>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>2.4</version>  
         <configuration>  
           <source>1.6</source>  
           <target>1.6</target>  
         </configuration>  
       </plugin>  
     </plugins>  
   </build>  
 </project>  

Step 2: Verifying the Flickr Endpoint

Click on the following link, this will generate a JSON output from Flickr.com.

http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?


Step 3: Defining the JSP


index.jsp
 <html>  
 <head>  
   <title>Flicker Image Library</title>  
   <script type="text/javascript" src="jquery.js"></script>  
   <script type="text/javascript">  
     // jQuery document ready
  
     $(function() {  
 /*  
            $("#tagDiv").ajaxStart(function(){  
             $('#loadImg').show();   
          });  
            $("#tagDiv").ajaxComplete(function(){  
             $('#loadImg').hide();   
          });  
 */  

       // when hit Enter on the text field  
       $('#tagField').keypress( function(evnt) {  
             var code = (evnt.keyCode ? evnt.keyCode : e.which);  
           // code to trap 'Enter' key  
           if( code == 13 ) {  
             getFlickrImages();  
           }  
       });  

       // when clicked on search button  
       $('#exploreBtn').click(function() {  
         getFlickrImages();  
       });  
     });  

     function getFlickrImages() {  
      var data = 'tags=' + $('#tagField').val().trim() + '&tagmode=any&format=json&jsoncallback=?';  
      $('#loadImg').show();  

     // sending a AJAX call to the flickr and expect JSON as return type  
      $.ajax({  
        type: 'GET',  
        url: 'http://api.flickr.com/services/feeds/photos_public.gne',  
        data: data,  
        dataType: 'json',  
        error: function(xhr, status, error) {  
             alert(status + '--> ' + error);  
           },  
        success: function(data) {  
             $('#loadImg').hide();                    
             $('#contentDiv').html(data.title);  
             $('#resultDiv').html('<center><table id="imageTable" style="border: 1px solid black; text-align:center"><tr></tr></table></center>');  
             $.each(data.items, function(index, item) {  
              // show FIVE images per row  
              if( index%5 == 0 ) {  
                   $('#imageTable tr:last').after('<tr></tr>')  
              }   
              //$('#imageTable tr:last').append('<td><img src=' + item.media.m + '></td>');    
              $('#imageTable tr:last').append('<td>' + item.description + '</td>');                           
             });  
         }  
      });  
     }  
   </script>  
 </head>  
 <body>  
 <h1 style="color: blue">Flicker Image Library</h1>  
 <div style="border: 1px solid blue" id="tagDiv">  
   <table style="color: blue">  
     <tr valign="middle">  
       <td><b>Tag Name</b></td>  
        <td><input type="text" id="tagField"/></td>  
       <td><button id="exploreBtn">Explore</button></td>  
       <td><img src="img/ajax-loader.gif" id="loadImg" style="display: none"/></td>  
     </tr>      
   </table>  
 </div>  
 <br>  
 <div id="contentDiv" style="border: 1px solid blue; color: blue"></div>  
 <div id="resultDiv"></div>  
 </body>  
 </html>  


The above Ajax call gives you more control over how you need to make the Ajax call. Conversely, you could also try the following:


var data = 'tags=' + $('#tagField').val().trim() + '&tagmode=any&format=json&jsoncallback=?';  

$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne',  
      data,  
      function(result) {  
        $('#loadImg').hide();                    
        $('#contentDiv').html(result.title);  
        $('#resultDiv').html('<center><table id="imageTable" style="border: 1px solid black; text-align:center"><tr></tr></table></center>');  
        $.each(result.items, function(index, item) {  
         // show FIVE images per row  
         if( index%5 == 0 ) {  
             $('#imageTable tr:last').after('<tr></tr>')  
         }   
         //$('#imageTable tr:last').append('<td><img src=' + item.media.m + '></td>');    
         $('#imageTable tr:last').append('<td>' + item.description + '</td>');                           
        });  
 });  
Step 4: Building and Deploying the Application

$ mvn clean install
$ mvn jetty:run

mvn:jetty:run - would run the web app on the jetty server.

Now, you can access the application with the following URL.
http://localhost:8080/org.fazlan.jquery.flickr.app

Summary:
This article looked at how jQuery can be used to easily manipulate Javascript and simplify the usage of Ajax API for JSON.

The sample code for this article can be found here.

Sunday, May 27, 2012

jQuery Tutorial: Ajax Processing - GET and POST

Overview:
This article will be on jQuery. jQuery simplifies the manipulation of DOM elements. Here, we'll be mainly focusing on how jQuery eases the usage of the Ajax API.

The sample code can be found here.

Application Demo:
The application is to add and display weather information about cities.

Home page:

Select City:



Display Selected City:


Adding a New City:



Submitting the New:


Project Structure:
 org.fazlan.jquery.app  
 |-- pom.xml  
 `-- src  
   `-- main  
     |-- java  
     |  `-- org  
     |    `-- fazlan  
     |      `-- jquery  
     |        `-- app  
     |          |-- domain  
     |          |  `-- Weather.java  
     |          `-- service  
     |            `-- WeatherService.java  
     |-- resources  
     `-- webapp  
       |-- img  
       |  `-- ajax-loader.gif  
       |-- index.jsp  
       |-- jquery.js  
       |-- jsp  
       |  |-- add_weather_ajaxprocessor.jsp  
       |  `-- list_weather_ajaxprocessor.jsp  
       `-- WEB-INF  
         `-- web.xml  

Step 1: Creating a the Web Project.

 mvn archetype:generate -DgroupId=org.fazlan -DartifactId=org.fazlan.spring.mvc -Dversion=1.0.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false  

Step 2: Updating the Maven Dependencies (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/maven-v4_0_0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan</groupId>  
   <artifactId>org.fazlan.jquery.app</artifactId>  
   <packaging>war</packaging>  
   <version>1.0.0-SNAPSHOT</version>  
   <name>org.fazlan.jquery.app Maven Webapp</name>  
   <url>http://maven.apache.org</url>  
   <dependencies>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>3.8.1</version>  
       <scope>test</scope>  
     </dependency>  
   </dependencies>  
   <build>  
     <finalName>jquery.app</finalName>  
     <plugins>  
       <plugin>  
         <groupId>org.mortbay.jetty</groupId>  
         <artifactId>maven-jetty-plugin</artifactId>  
         <version>6.1.10</version>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>2.4</version>  
         <configuration>  
           <source>1.6</source>  
           <target>1.6</target>  
         </configuration>  
       </plugin>  
     </plugins>  
   </build>  
 </project>  

Step 3: Defining the Java Backend Logic

Weather.java
 package org.fazlan.jquery.app.domain;  
 public class Weather {  
   private String code;  
   private String city;  
   private String description;  
   private int temperature;  
   private int humidity;  
   public Weather(String code, String city, String description, int temperature, int humidity) {  
     this.code = code;  
     this.city = city;  
     this.description = description;  
     this.temperature = temperature;  
     this.humidity = humidity;  
   }  
   public String getCode() {  
     return code;  
   }  
   public String getCity() {  
     return city;  
   }  
   public String getDescription() {  
     return description;  
   }  
   public int getTemperature() {  
     return temperature;  
   }  
   public int getHumidity() {  
     return humidity;  
   }  
   @Override  
   public String toString() {  
     return "Weather{" +  
         "code='" + code + '\'' +  
         ", city='" + city + '\'' +  
         ", description='" + description + '\'' +  
         ", temperature=" + temperature +  
         ", humidity=" + humidity +  
         '}';  
   }  
 }  

WeatherService.java
 package org.fazlan.jquery.app.service;  
 import org.fazlan.jquery.app.domain.Weather;  
 import java.util.HashMap;  
 import java.util.Map;  
 public class WeatherService {  
   private static final Map<String, Weather> weather_map = new HashMap<String, Weather>();  
   static {  
     weather_map.put("SYD", new Weather("SYD", "Sydney", "Sunny", 22, 50));  
     weather_map.put("MEL", new Weather("MEL", "Melbourne", "Cloudy", 14, 45));  
     weather_map.put("QLD", new Weather("QLD", "Queensland", "Hot and Humid", 26, 65));  
     weather_map.put("PER", new Weather("PER", "Perth", "Sunny", 28, 55));  
     weather_map.put("ADL", new Weather("ADL", "Adelaide", "Mild", 18, 45));  
     weather_map.put("DWN", new Weather("DWN", "Darwin", "Sunny", 24, 45));  
     weather_map.put("TAS", new Weather("TAS", "Tasmania", "Cold", 12, 35));  
   }  
   public Weather get(String code) {  
     return weather_map.get(code);  
   }  
   public void add(Weather weather) {  
     weather_map.put(weather.getCode(), weather);  
   }  
 }  

Step 4: Defining the JSPs for Processing Requests
These JSP pages will act as Controllers, and invoke the backend business logic classes to process the requests.

list_weather_ajaxprocessor.jsp - This handles the requests for retrieving information related to the selected city from the dropdown.

 <%@ page import="org.fazlan.jquery.app.service.WeatherService" %>  
 <%@ page import="org.fazlan.jquery.app.domain.Weather" %>  
 <%!  
   // create an instance of the service  
   WeatherService weatherService = new WeatherService();  
   Weather weatherResult = null;  
 %>  
 <%  
   // get the selected city code from the request  
   String cityCode = request.getParameter("cityCode");  
   System.out.println("City Code: " + cityCode);  
   if (cityCode != null && !"".equals(cityCode)) {  
     // get the corresponding weather details for the requested citycode  
     weatherResult = weatherService.get(cityCode);  
     // delay to show the progress image in the UI using Ajax/jQuery  
     try {  
       Thread.sleep(2000);  
     } catch (Exception e) {  
     }  
   } else {  
     return;  
   }  
 %>  
 <!-- put the result into a HTML table and return that-->  
 <table width="500px">  
   <tr>  
     <td width="40%"><b>City</b></td>  
     <td style="color: blue;"><%= weatherResult.getCity() %>  
     </td>  
   </tr>  
   <tr>  
     <td width="40%"><b>Description</b></td>  
     <td style="color: blue;"><%= weatherResult.getDescription() %>  
     </td>  
   </tr>  
   <tr>  
     <td width="40%"><b>Temperature</b></td>  
     <td style="color: blue;"><%= weatherResult.getTemperature() %>  
     </td>  
   </tr>  
   <tr>  
     <td width="40%"><b>Humidity</b></td>  
     <td style="color: blue;"><%= weatherResult.getHumidity()%>  
     </td>  
   </tr>  
 </table>  

add_weather_ajaxprocessor.jsp - This handles the requests for submitting information related to the new city.
 <%@ page import="org.fazlan.jquery.app.service.WeatherService" %>  
 <%@ page import="org.fazlan.jquery.app.domain.Weather" %>  
 <%!  
   WeatherService weatherService = new WeatherService();  
 %>  
 <%  
   // get the form parameters  
   String code = request.getParameter("code");  
   String city = request.getParameter("city");  
   String description = request.getParameter("description");  
   int temperature = Integer.parseInt(request.getParameter("temperature"));  
   int humidity = Integer.parseInt(request.getParameter("humidity"));  
   // create a new Weather object and populate with the form values  
   Weather weather = new Weather(code, city, description, temperature, humidity);  
   // delay to show the progress image in the UI using Ajax/jQuery  
   try {  
     Thread.sleep(2000);  
   } catch (Exception e) {  
   }  
   // add the new Weather information  
   weatherService.add(weather);  
   // return the new city code to be added to the dropdown list  
   out.println("<option value=" + weather.getCode() + "> " + weather.getCity() + "</option>");  
 %>  

Step 5: Defining the Actual JSP View
Index.jsp
 <html>  
 <head>  
   <script type="text/javascript" src="jquery.js"></script>  
   <script type="text/javascript">  
     // jQuery document ready  
     $(function() {  
       /* jQuery event handling for city dropdown list */  
       $('#citySelect').change(function() {  
         $('#newEntryDiv').hide();  
         var cityCd = $(this).val();  
         $('#loadImg').show();  
         $.get(  
           'jsp/list_weather_ajaxprocessor.jsp',  
           {  
             cityCode: cityCd  
           },  
           function(data) {  
              $('#loadImg').hide();  
              $('#contentDiv').html(data);  
           });  
       });  
       /* jQuery event handling for adding a new city entry */  
       $('#addbtn').click(function() {  
         $('#newEntryDiv').show();  
         $('#contentDiv').html('');  
       });  
       /* jQuery event handling for submitting the new entry */  
       $('#newEntryForm').submit(function() {  
         $('#saveImg').show();  
         $.post(  
           'jsp/add_weather_ajaxprocessor.jsp',  
           $('#newEntryForm').serialize(),  
           function(data) {  
              $('#saveImg').hide();  
              $('#citySelect').append(data);  
              $('#newEntryDiv').hide();  
           });  
           clearForm($(this));  
         /*DO NOT REFRESH page after submit*/  
         return false;  
       });  
     });  
     /** function to clear the form */  
     function clearForm(form) {  
       $(":input", form).each(function() {  
         var type = this.type;  
         var tag = this.tagName.toLowerCase();  
         if (type == 'text') {  
           this.value = "";  
         }  
       });  
     }  
   </script>  
 </head>  
 <body>  
 <h1>Weather in Australia and Around the Globe</h1>  
 <div id="formDiv" style="border: 1px solid blue">  
   <table>  
     <tr valign="middle">  
       <td><b>Select City</b></td>  
       <td>  
         <form>  
           <select name="citySelect" id="citySelect">  
             <option value="">--- Select ---</option>  
             <option value="ADL">Adelaide</option>  
             <option value="DWN">Darwin</option>  
             <option value="MEL">Melbourne</option>  
             <option value="PER">Perth</option>  
             <option value="QLD">Queensland</option>  
             <option value="SYD">Sydney</option>  
             <option value="TAS">Tasmania</option>  
           </select>  
         </form>  
       </td>  
       <td><img src="img/ajax-loader.gif" style="display:none;" id="loadImg"/></td>  
     </tr>  
     <tr>  
       <td colspan="3">  
         <button id="addbtn" type="submit">Add New</button>  
       </td>  
     </tr>  
   </table>  
 </div>  
 <br>  
 <div id="newEntryDiv" style="display: none; border: 1px solid blue">  
   <form id="newEntryForm">  
     <table width="500 px">  
       <tr>  
         <td width="40%"><b>Code</b></td>  
         <td><input type="text" name="code"></td>  
       </tr>  
       <tr>  
         <td width="40%"><b>City</b></td>  
         <td><input type="text" name="city"></td>  
       </tr>  
       <tr>  
         <td width="40%"><b>Description</b></td>  
         <td><input type="text" name="description"></td>  
       </tr>  
       <tr>  
         <td width="40%"><b>Temperature</b></td>  
         <td><input type="text" name="temperature"></td>  
       </tr>  
       <tr>  
         <td width="40%"><b>Humidity</b></td>  
         <td><input type="text" name="humidity"></td>  
       </tr>  
       <tr>  
         <td><input type="submit"></td>  
         <td><img src="img/ajax-loader.gif" style="display:none;" id="saveImg"/></td>  
       </tr>  
     </table>  
   </form>  
 </div>  
 <div id="contentDiv" style="border: 1px solid blue">Weather information will appear here.</div>  
 </body>  
 </html>  


Step 6: Building and Deploying the Application

$ mvn clean install
$ mvn jetty:run

mvn:jetty:run - would run the web app on the jetty server.

Now, you can access the application with the following URL.
http://localhost:8080/org.fazlan.jquery.app/

Summary:
This article looked at how jQuery can be used to easily manipulate Javascript and simplify the usage of Ajax API.

The sample code can be found here.