Thursday, July 7, 2011

JAX-RS REST FileUpload and FileDownload example

In the process of comparing REST vs SOAP one of the test case for evaluation was document management service. i.e., upload and download files via services. I have previously consumed SOAP based service but it was having some serious limitations and performance issues since we have to read the complete file before writing it to output stream and has unnecessary data hops. Our goal is to deploy a simple lightweight services, with high performance dealing with documents using Jersey JAX-RS implementation.

Test case: Simple directory listing of files in a grid with an ability to upload, download and delete file as RESTful services.

Output looks some thing like this:



POM file

<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>wlrestgrid</groupId>
 <artifactId>wlrestgrid</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>war</packaging>
 <name />
 <description />
 
 <dependencies>
  <dependency>
   <groupId>javax.xml.bind</groupId>
   <artifactId>jaxb-api</artifactId>
   <version>2.1</version>
  </dependency>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-server</artifactId>
   <version>1.8</version>
  </dependency>
  <dependency>
   <groupId>asm</groupId>
   <artifactId>asm</artifactId>
   <version>3.1</version>
  </dependency>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-json</artifactId>
   <version>1.8</version>
  </dependency>
  <dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-multipart</artifactId>
    <version>1.8</version>
  </dependency>
  <dependency>
   <groupId>org.codehaus.jettison</groupId>
   <artifactId>jettison</artifactId>
   <version>1.3</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.0.1</version>
  </dependency>
  <dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.2.2</version>
  </dependency>
 </dependencies>
 <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>http://download.java.net/maven/2/</url>
            <layout>default</layout>
        </repository>
    </repositories>
 <build>
  <sourceDirectory>${basedir}/src</sourceDirectory>
  <outputDirectory>${basedir}/WebRoot/WEB-INF/classes</outputDirectory>
  <resources>
   <resource>
    <directory>${basedir}/src</directory>
    <excludes>
     <exclude>**/*.java</exclude>
    </excludes>
   </resource>
  </resources>
  <plugins>
  </plugins>
 </build>
</project>


web.xml file has configurations for Jersey see line 13 it is the package where the services are defined.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <display-name></display-name>
 <servlet>
  <display-name>JAX-RS REST Servlet</display-name>
  <servlet-name>JAX-RS REST Servlet</servlet-name>
  <servlet-class>com.sun.jersey.server.impl.container.servlet.ServletAdaptor</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>com.dvmr.poc.rest</param-value>
        </init-param>
        <init-param>
            <param-name>com.sun.jersey.config.feature.Redirect</param-name>
            <param-value>true</param-value>
        </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>JAX-RS REST Servlet</servlet-name>
  <url-pattern>/services/*</url-pattern>
 </servlet-mapping>
 <welcome-file-list>
  <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>
</web-app>


DocumentService.java has all the RESTful services defined to list, upload, download and delete file from a directory all the paths are highlighted.

package com.dvmr.poc.rest;

import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;

import com.dvmr.poc.bean.FileBean;
import com.dvmr.poc.exception.NotFoundException;
import com.dvmr.poc.service.BlobService;
import com.dvmr.poc.service.impl.BlobServiceImpl;
import com.dvmr.poc.util.MultipartUtil;

/**
 * This rest based document service
 * @author vreddy.fp
 *
 */

@Path("/document")
public class DocumentService {

 /**
  * Replace with Inject annotation in JEE6 or with Spring 3.0
  */
 private final BlobService blobService = new BlobServiceImpl();

 public DocumentService() {
  super();
 }
 
 public BlobService getBlobService() {
  return blobService;
 }


 @POST
 @Path("upload")
 @Consumes(MediaType.MULTIPART_FORM_DATA)
 @Produces(MediaType.TEXT_PLAIN)
 public Response uploadFile(@Context HttpServletRequest request,
   @Context HttpServletResponse res) throws Exception {
  String response = "Unable to attach files";
  FileBean bean = MultipartUtil.parseMultipart(request, getBlobService());
  if (null != bean) {
   response = "{\"name\":\"" + bean.getFilename() + "\",\"type\":\""
   + bean.getContentType() + "\",\"size\":\"" + bean.getSize()
   + "\"}";
  }
  return Response.ok(response).build();
 }

 /**
  * In Memory solution
  * 
  * @param blobKey
  * @return
  * @throws Exception
  */
 @GET
 @Path("download")
 @Produces(MediaType.APPLICATION_OCTET_STREAM)
 public Response downloadFile(
   @DefaultValue("empty") @QueryParam(value = "blobKey") String blobKey)
   throws Exception {
  if(blobKey.equals("empty"))
   throw new NotFoundException("blobKey cannot be empty!");
  
  byte[] docStream = getBlobService().getBlob(blobKey);
  return Response
    .ok(docStream, MediaType.APPLICATION_OCTET_STREAM)
    .header("content-disposition", "attachment; filename = " + blobKey).build();
 }
 
 /**
  * list all valid files in a directory
  * @return
  * @throws JSONException
  */
 @GET
 @Path("list")
 @Produces( { MediaType.APPLICATION_JSON})
 public JSONArray listFiles() throws JSONException{
  JSONArray arr = new JSONArray();
  List<FileBean> list = getBlobService().getBlobs();
  for(Iterator<FileBean> i = list.iterator(); i.hasNext();){
   arr.put(i.next().toJson());
  }
  return arr;
 }
 
 /**
  * remove file from directory
  * @param blobKey
  * @return
  */
 @GET
 @Path("delete")
 @Produces(MediaType.TEXT_PLAIN)
 public Response deleteFile(@DefaultValue("empty") @QueryParam(value = "blobKey") String blobKey){
  
  if(blobKey.equals("empty"))
   throw new NotFoundException("blobKey cannot be empty!");
  
  getBlobService().deleteBlob(blobKey);
  return Response.status(Status.OK).build();
 }
 
}


FileBean.java is a simple POJO that holds file details.

package com.dvmr.poc.bean;

import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;

import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

/**
 * 
 * @author vreddy.fp
 *
 */
@XmlRootElement
public class FileBean {
 String filename;
 long size;
 String url;
 String contentType;
 Map<String, String> formFieldsMap;

 public FileBean(String filename, long size, String url, String contentType) {
  this.filename = filename;
  this.size = size;
  this.url = url;
  this.contentType = contentType;
 }

 public FileBean() {
 }

 

 public String getFilename() {
  return filename;
 }

 public void setFilename(String filename) {
  this.filename = filename;
 }

 public long getSize() {
  return size;
 }

 public void setSize(long size) {
  this.size = size;
 }

 public String getUrl() {
  return url;
 }

 public void setUrl(String url) {
  this.url = url;
 }

 public String getContentType() {
  return contentType;
 }

 public void setContentType(String contentType) {
  this.contentType = contentType;
 }

 public Map<String, String> getFormFieldsMap() {
  return formFieldsMap;
 }

 public void setFormFieldsMap(Map<String, String> formFieldsMap) {
  this.formFieldsMap = formFieldsMap;
 }
 
 public JSONObject toJson() throws JSONException {
  JSONObject obj = new JSONObject();
  obj.put("name", filename);
  obj.put("size", new Long(size));
  obj.put("url", url);
  obj.put("contentType", contentType);
  return obj;
 }
 
}


MultipartUtil.java has the method to parse the request using commons streaming API for faster uploads and better performance for reading files.

package com.dvmr.poc.util;

import java.io.InputStream;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.IOUtils;

import com.dvmr.poc.bean.FileBean;
import com.dvmr.poc.service.BlobService;

/**
 * 
 * @author vreddy.fp
 *
 */
public class MultipartUtil {

 /**
  * 
  * @param request
  * @return
  */
 public static FileBean parseMultipart(HttpServletRequest request, BlobService blobService){
  FileBean bean = null;
  if (ServletFileUpload.isMultipartContent(request)) { 
   bean = new FileBean();
            ServletFileUpload uploadHandler = new ServletFileUpload();
            InputStream stream = null;
            bean.setFormFieldsMap(new HashMap<String, String>());
            try {
                FileItemIterator itr = uploadHandler.getItemIterator(request);
                while(itr.hasNext()) {
                 FileItemStream item = itr.next();
                 String name = item.getFieldName(); // form field name
                    stream = item.openStream();
                    if(item.isFormField()) {
                     String value = Streams.asString(stream);
                     bean.getFormFieldsMap().put(name, value);
                    } else {
                       bean.setFilename(item.getName());
                         bean.setContentType(item.getContentType());
                         bean.setSize(blobService.uploadBlob(stream, item.getName()));
                    }
                }
            }catch(FileUploadException ex) {
             ex.printStackTrace();
            } catch(Exception ex) {
             ex.printStackTrace();
            }finally{
             IOUtils.closeQuietly(stream);
            }
        } 
  return bean;
 }
}

BlobService.java is the interface.

package com.dvmr.poc.service;

import java.io.InputStream;
import java.util.List;

import com.dvmr.poc.bean.FileBean;

public interface BlobService {
 /**
  * upload files to a directory
  * @param inputStream
  * @param filename
  * @return
  */
 public long uploadBlob(InputStream inputStream, String filename);
 /**
  * returns the file as a byte[]
  * @param blobKey
  * @return
  */
 public byte[] getBlob(String blobKey);
 
 /**
  * Deletes file form the directory
  * @param blobKey
  */
 public void deleteBlob(String blobKey);
 
 /**
  * get all files from the directory
  * @return
  */
 public List<FileBean> getBlobs();
}


BlobServiceImpl.java is the implementation for BlobService

package com.dvmr.poc.service.impl;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CountingInputStream;

import com.dvmr.poc.bean.FileBean;
import com.dvmr.poc.service.BlobService;
import com.dvmr.poc.util.PropertyFileLoader;

/**
 * 
 * @author vreddy.fp
 *
 */
public class BlobServiceImpl implements BlobService {

 private PropertyFileLoader propertyFileLoader = PropertyFileLoader.getInstance("config");

 public long uploadBlob(InputStream inputStream, String filename) {
  Writer output = null;
  CountingInputStream countingInputStream = null;
  long filesize = 0;
  try {
   // if uploading form IE it get complete path
   filename = FilenameUtils.getName(filename);
   output = new FileWriter(new File(getDirctoryLoation()+ filename));
   countingInputStream = new CountingInputStream(inputStream);
   IOUtils.copy(countingInputStream, output);
   filesize = countingInputStream.getByteCount();
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   IOUtils.closeQuietly(countingInputStream);
   IOUtils.closeQuietly(output);
  }
  return filesize;
 }

 /**
  * 
  */
 public byte[] getBlob(String blobKey) {
  File file = new File(getDirctoryLoation()+ blobKey);
  byte[] docStream = null;
  try {
   docStream = FileUtils.readFileToByteArray(file);
  } catch (IOException e) {
   e.printStackTrace();
  }
  return docStream;
 }

 public void deleteBlob(String blobKey) {
  FileUtils.deleteQuietly(new File(getDirctoryLoation()+ blobKey));
 }

 /**
  * 
  */
 public List<FileBean> getBlobs() {
 
  List<FileBean> list = new ArrayList<FileBean>();
  Iterator<File> files = FileUtils.iterateFiles(
    new File(getDirctoryLoation()),
    getValidFileExtentions(),
    false);

  while (files.hasNext()) {
   File file = files.next();
   FileBean fileBean = new FileBean(
     file.getName(), 
     file.length(),
     file.getAbsolutePath(), 
     FilenameUtils.getExtension(file.getName()));
   list.add(fileBean);
  }
  return list;
 }

 public String getDirctoryLoation() {
  return propertyFileLoader.getValue("directory.location");
 }

 public String[] getValidFileExtentions() {
  return propertyFileLoader.getValue("valid.file.extentions").split("\\|");
 }

}

PropertyFileLoader.java is Util class to loads config.properties file. This file as the Directory location to where the files are written and displayed.

package com.dvmr.poc.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.commons.io.IOUtils;

/**
 * 
 * @author vreddy.fp
 *
 */
public class PropertyFileLoader {

 private static PropertyFileLoader instance = null;
 private static final String PROPERTIES_FILE = ".properties";
 Properties properties = null;

 
 public PropertyFileLoader() {
  super();
 }
 
 public static PropertyFileLoader getInstance(String name) {
       if(instance == null) {
          instance = new PropertyFileLoader(name);
       }
       return instance;
    }

 public PropertyFileLoader(String name) {
  loadProperties(name);
 }

 public void loadProperties(String name) {
  properties = new Properties();
  InputStream in = this.getClass().getResourceAsStream("/com/dvmr/poc/resources/" + name + PROPERTIES_FILE);
  try {
   properties.load(in);
  } catch (IOException e) {
   e.printStackTrace();
  }finally{
   IOUtils.closeQuietly(in);
  }

 }
 
 public String getValue(String key){
  return properties.getProperty(key);
 }
}


config.properties file is defined in com.dvmr.poc.resources package.

directory.location=C\:\\temp\\logs\
valid.file.extentions=gif|jpg|JPG|png|txt|jpeg

fileutil.jsp file uses JQuery for JSOP, SlickGrid for to list the files in grid and jquery.form.js plugin to upload files using ajax. Copy necessary plugin files including css and image files to your web application.

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
 String path = request.getContextPath();
 String basePath = request.getScheme() + "://"
   + request.getServerName() + ":" + request.getServerPort()
   + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
  <base href="<%=basePath%>">

  <title>My JSP 'fileutil.jsp' starting page</title>

  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0">
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="This is my page">
  <link rel="stylesheet" href="css/slickgrid/slick.grid.css" type="text/css" media="screen" charset="utf-8" />
        <link rel="stylesheet" href="css/smoothness/jquery-ui-1.8.5.custom.css" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="css/slickgrid/examples.css" type="text/css" media="screen" charset="utf-8" />
 </head>

 <body>
  <h3>Directory Browser that displays only (gif, jpg, JPG, png, txt, jpeg)</h3>
  <table width="100%">
  <tr>
   <td valign="top" width="50%">
    <div id="myGrid" style="width:600px;height:500px;"></div>
   </td>
  </tr>
  </table>
  <hr>
  <h3>Upload file (Uses Iframe)</h3>
  <form id="myform" action="services/document/upload" method="POST" enctype="multipart/form-data">
   <div id="fileSection">
    File:
    <input type="file" name="fileup0" />
   </div>
   <input type="submit" value="Upload file" />
  </form>


 </body>
 
     <script language="JavaScript" src="lib/jquery-1.4.3.min.js"></script> 
  <script language="JavaScript" src="lib/jquery-ui-1.8.5.custom.min.js"></script> 
  <script language="JavaScript" src="lib/jquery.event.drag-2.0.min.js"></script> 
  <script language="JavaScript" src="lib/jquery.form.js"></script>
        
        <script language="JavaScript" src="javascripts/slickgrid/slick.core.js"></script> 
  <script language="JavaScript" src="javascripts/slickgrid/slick.editors.js"></script> 
  <script language="JavaScript" src="javascripts/slickgrid/slick.grid.js"></script> 
 <script>
 $(function() {
  loadGrid();
  ajaxFileUpload();
  downloadFile();
  deleteFile();
 });
 
 
 function downloadFile(){
  $(".download-file").click(function(event) {
   event.preventDefault();
   if ($("#downloadFrame").length == 0) {
    $('<iframe id="downloadFrame" style="display:none;" src="about:none"></iframe>').appendTo('body');
   }
   $("#downloadFrame").attr("src", $(this).attr("href"));
  });
 }
 
 function deleteFile(){
  $(".delete-file").live("click", function(event){
    event.preventDefault();
    $.getJSON($(this).attr("href"), function(vals) {
      loadGrid();
  });
  });
 }
 
 function ajaxFileUpload(){
  function cb_success (rt, st, xhr, wf) {
     loadGrid();
     $('#myform').resetForm();
  }
  var options = {
         success: cb_success,
         dataType: 'html',
         contentType: 'text/plain',
         method: 'POST'
     };
     $('#myform').ajaxForm(options);
 }
 
 function loadGrid(){
   var grid;
  var data = [];
  var downloadFileFormatter = function(row, cell, value, columnDef, dataContext) {
      return "<a class='download-file' href='services/document/download?blobKey=" + dataContext["name"] + "'>Download</a>" +
      "&nbsp;<a class='delete-file' href='services/document/delete?blobKey=" + dataContext["name"] + "'>Delete</a>";
  };
  var columns = [
   {id:"download", name:"Actions", formatter:downloadFileFormatter, width:110},
   {id:"name", name:"Name", field:"name", width:120, cssClass:"cell-title"},
   {id:"size", name:"Size", field:"size"},
   {id:"url", name:"Path", field:"url", width:200},
   {id:"contentType", name:"Type", field:"contentType", width:50}
  ];
 
  var options = {
   enableAddRow: false,
   enableCellNavigation: true
  };
  
   $.getJSON("services/document/list?timire="+Math.min(100, Math.round(Math.random() * 110)) ,  function(vals){
                $.each(vals, function() { 
                    data.push(this); 
                }); 
                grid = new Slick.Grid($("#myGrid"), data, columns, options); 
            }); 
 }
</script>
</html>

Source code is available @ GitHub finally :-)

Monday, June 27, 2011

Weblogic 10.3.4+ Jersey JAX-RS + JSON + SlickGrid

I want to try some RESTful services returning JSON response and use response in JS widgets as part of POC to propose them as an alternative to SOAP based services. Many developers have implemented similar functionality before and we can find many examples on the web (google). But I wanted to try it out myself before I agree with other developers who advocate for REST bases services and to be honest I am convinced after building a simple example application in less than 2 hrs.

My goal is to deploy the example application in Oracle weblogic 10.3.4 and use Jersey JAX-RS 1.5 implementation as this implementation is supported by weblogic and comes as part of shared library. Below are the sequence of steps that I have followed:

1. Enable Jersey JAX-RS implementation in weblogic. Link

2. Setup Maven for weblogic 10.3.4. Link

3. Create a Dynamic webproject in MyEclipse and create a simple service that returns JSON array as response.

POM file

  <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>wlrestgrid</groupId>
 <artifactId>wlrestgrid</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>war</packaging>
 <name />
 <description />
 <dependencies>
  <dependency>
   <groupId>javax.xml.bind</groupId>
   <artifactId>jaxb-api</artifactId>
   <version>2.1</version>
  </dependency>
  <dependency>
   <groupId>com.sun.xml.bind</groupId>
   <artifactId>jaxb-impl</artifactId>
   <version>2.1.12</version>
  </dependency>
  <dependency>
   <groupId>javax.activation</groupId>
   <artifactId>activation</artifactId>
   <version>1.1</version>
  </dependency>
  <dependency>
   <groupId>javax.xml.stream</groupId>
   <artifactId>stax-api</artifactId>
   <version>1.0-2</version>
  </dependency>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-core</artifactId>
   <version>1.4</version>
  </dependency>
  <dependency>
   <groupId>javax.ws.rs</groupId>
   <artifactId>jsr311-api</artifactId>
   <version>1.1.1</version>
  </dependency>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-server</artifactId>
   <version>1.4</version>
  </dependency>
  <dependency>
   <groupId>asm</groupId>
   <artifactId>asm</artifactId>
   <version>3.1</version>
  </dependency>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-client</artifactId>
   <version>1.4</version>
  </dependency>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-json</artifactId>
   <version>1.4</version>
  </dependency>
  <dependency>
   <groupId>org.codehaus.jettison</groupId>
   <artifactId>jettison</artifactId>
   <version>1.1</version>
  </dependency>
 </dependencies>
 <build>
  <sourceDirectory>${basedir}/src</sourceDirectory>
  <outputDirectory>${basedir}/WebRoot/WEB-INF/classes</outputDirectory>
  <resources>
   <resource>
    <directory>${basedir}/src</directory>
    <excludes>
     <exclude>**/*.java</exclude>
    </excludes>
   </resource>
  </resources>
  <plugins>
   <plugin>
    <groupId>com.oracle.weblogic</groupId>
    <artifactId>weblogic-maven-plugin</artifactId>
    <version>10.3.4</version>
    <configuration>
     <adminurl>t3://localhost:7001</adminurl>
     <user>weblogic</user>
     <password>WEBLOGIC_PASSWORD</password>
     <upload>true</upload>
     <action>deploy</action>
     <remote>false</remote>
     <verbose>true</verbose>
     <source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
     <name>${project.build.finalName}</name>
    </configuration>
    <executions>
     <execution>
      <phase>install</phase>
      <goals>
       <goal>deploy</goal>
      </goals>
     </execution>
    </executions>
   </plugin>
  </plugins>
 </build>a
</project>

web.xml will have JAX-RS servlet mapping

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <display-name></display-name>
 <servlet>
  <display-name>JAX-RS REST Servlet</display-name>
  <servlet-name>JAX-RS REST Servlet</servlet-name>
  <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>JAX-RS REST Servlet</servlet-name>
  <url-pattern>/services/*</url-pattern>
 </servlet-mapping>
 <welcome-file-list>
  <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>
</web-app>


HelloWorld.java

package com.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;

import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;

import com.bean.GridObject;


@Path("/helloworld")
public class HelloWorld {
 @Context
 private UriInfo context;

 public HelloWorld() {
 }

 @GET
 @Path("getGridData")
 @Produces( { MediaType.APPLICATION_JSON})
 public JSONArray getGridByJson() throws JSONException{
  JSONArray arr = new JSONArray();
  for(int i=0; i< 100; i++){
      arr.put(new GridObject("Task "+i, "5 days", i * 1, "01/01/2009", "01/05/2009", i % 5==0).toJson());
    }
        return arr;
 }
 
}

GridObject.java is a simple POJO object with toJson() method to return json object

package com.bean;

import javax.xml.bind.annotation.XmlRootElement;

import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

@XmlRootElement
public class GridObject {
 private String title, duration;
 private int percentComplete;
 private boolean effortDriven;
 private String start, finish;

 
 public GridObject() {
  super();
  // TODO Auto-generated constructor stub
 }

 public GridObject(String title, String duration, int percentComplete,
   String start, String finish, boolean effortDriven) {
  this.title = title;
  this.duration = duration;
  this.percentComplete = percentComplete;
  this.start = start;
  this.finish = finish;
  this.effortDriven = effortDriven;
 }

 public String getTitle() {
  return title;
 }

 public void setTitle(String title) {
  this.title = title;
 }

 public String getDuration() {
  return duration;
 }

 public void setDuration(String duration) {
  this.duration = duration;
 }

 public int getPercentComplete() {
  return percentComplete;
 }

 public void setPercentComplete(int percentComplete) {
  this.percentComplete = percentComplete;
 }

 public boolean isEffortDriven() {
  return effortDriven;
 }

 public void setEffortDriven(boolean effortDriven) {
  this.effortDriven = effortDriven;
 }

 public String getStart() {
  return start;
 }

 public void setStart(String start) {
  this.start = start;
 }

 public String getFinish() {
  return finish;
 }

 public void setFinish(String finish) {
  this.finish = finish;
 }

 public JSONObject toJson() throws JSONException {
  JSONObject obj = new JSONObject();
  obj.put("title", title);
  obj.put("duration", duration);
  obj.put("percentComplete", new Integer(percentComplete));
  obj.put("start", start);
  obj.put("finish", finish);
  obj.put("effortDriven", new Boolean(effortDriven));
  return obj;
 }

}



4. Create a JSP page that invokes the RESTful service using JQuery.getJSON() method to get the response and use this response to generate the slickgrid. Why slickgrid?? I like it after seeing the comprehensive exmaple.

Copy all necessary javascript, css and image files from slickgrid to your web applicaiton.

slickgrid.jsp

<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  <title>SlickGrid example 1: Basic grid</title>
  <link rel="stylesheet" href="css/slickgrid/slick.grid.css" type="text/css" media="screen" charset="utf-8" />
        <link rel="stylesheet" href="css/smoothness/jquery-ui-1.8.5.custom.css" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="css/slickgrid/examples.css" type="text/css" media="screen" charset="utf-8" />
 </head>
 <body>
  <table width="100%">
  <tr>
   <td valign="top" width="50%">
    <div id="myGrid" style="width:600px;height:500px;"></div>
   </td>
   <td valign="top">
    <h2>Demonstrates:</h2>
    <ul>
     <li>basic grid with minimal configuration</li>
    </ul>
   </td>
  </tr>
  </table>

  <script language="JavaScript" src="lib/jquery-1.4.3.min.js"></script> 
  <script language="JavaScript" src="lib/jquery-ui-1.8.5.custom.min.js"></script> 
  <script language="JavaScript" src="lib/jquery.event.drag-2.0.min.js"></script> 
        
        <script language="JavaScript" src="javascripts/slickgrid/slick.core.js"></script> 
  <script language="JavaScript" src="javascripts/slickgrid/slick.editors.js"></script> 
  <script language="JavaScript" src="javascripts/slickgrid/slick.grid.js"></script> 
  <script>

  var grid;
  var data = [];
  var columns = [
   {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title"},
   {id:"duration", name:"Duration", field:"duration"},
   {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter},
   {id:"start", name:"Start", field:"start", minWidth:60},
   {id:"finish", name:"Finish", field:"finish", minWidth:60},
   {id:"effort-driven", name:"Effort Driven", sortable:false, width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter}
  ];
 
  var options = {
   //editable: false,
   enableAddRow: false,
   enableCellNavigation: true
  };

  $(function() {
       //http://localhost:8080/wlrestgrid/services/helloworld/getGridData
             $.getJSON("services/helloworld/getGridData?timire="+Math.min(100, Math.round(Math.random() * 110)) , 
                function(vals){
                $.each(vals, function() { 
                    data.push(this); 
                }); 
                grid = new Slick.Grid($("#myGrid"), data, columns, options); 
            }); 
        });

  </script>

 </body>
</html>



5. Deploy the application to Oracle weblogic server using Maven and open the slickgrid.jsp file in the browser.

Tuesday, June 21, 2011

Oracle Weblogic 10.3.4

After installing Oracle Weblogic 10.3.4 zip installation on Window XP I was wondering how to create JNDI Data Source using the admin console and did not find a way. So finally I have to write my WLST script and after that I started to like WLST and was amazed with so many things that you can do with this little tool. Below is the WLST script to create Datasource using properties file.

1. Save WLST as CreateDataSource.py
2. Save the below properties file as domain.properties
3. For running WLST scripts follow this link

#Conditionally import wlstModule only when script is executed with jython
if __name__ == '__main__': 
    from wlstModule import *#@UnusedWildImport

from java.io import FileInputStream

propInputStream = FileInputStream("PATH_TO_PROP_FILES/domain.properties")
configProps = Properties()
configProps.load(propInputStream)

print 'starting the script ....'

connect(configProps.get("domain.admin.username"),
        configProps.get("domain.admin.password"), 
        configProps.get("domain.admin.url"))


edit()
startEdit()

server= configProps.get("domain.server.name")

print 'server is ' + server

cd("Servers/"+server)
target=cmo
cd("../..")

dsname=configProps.get("domain.ds.name")
jndiname=configProps.get("domain.jndi.name")
jdbc_url =configProps.get("domain.ds.jdbc.url")
jdbc_driver= configProps.get("domain.ds.jdbc.driver") 
jdbc_user=configProps.get("domain.ds.jdbc.user")
jdbc_pwd=configProps.get("domain.ds.jdbc.pwd")

# start creation
print 'Creating JDBCSystemResource with name '+dsname
jdbcSR = create(dsname,"JDBCSystemResource")
theJDBCResource = jdbcSR.getJDBCResource()
theJDBCResource.setName(dsname)

connectionPoolParams = theJDBCResource.getJDBCConnectionPoolParams()
connectionPoolParams.setConnectionReserveTimeoutSeconds(25)
connectionPoolParams.setMaxCapacity(100)
connectionPoolParams.setTestTableName("SQL SELECT 1 FROM DUAL")
connectionPoolParams.setConnectionCreationRetryFrequencySeconds(100);

dsParams = theJDBCResource.getJDBCDataSourceParams()
dsParams.addJNDIName(jndiname)


driverParams = theJDBCResource.getJDBCDriverParams()
driverParams.setUrl(jdbc_url)
driverParams.setDriverName(jdbc_driver)

driverParams.setPassword(jdbc_pwd)
driverProperties = driverParams.getProperties()

proper = driverProperties.createProperty("user")
proper.setValue(jdbc_user)

jdbcSR.addTarget(target)

save()
activate(block="true")

print 'Done configuring the data source'

Below are the properties files

###################
 Domain-1 Details
###################
domain.name=mydomain
domain.admin.url=t3://localhost:7001
domain.admin.username=weblogic
domain.admin.password=WL_PASSWORD
domain.server.name=myserver

domain.ds.name=JDBCDataSource
domain.jndi.name=jdbc.tempDS 


domain.ds.jdbc.url=jdbc:oracle:thin:@SERVER_NAME:1521:SERVER_INSTANCE
domain.ds.jdbc.driver=oracle.jdbc.driver.OracleDriver
domain.ds.jdbc.user=scott
domain.ds.jdbc.pwd=tiger

Tuesday, August 24, 2010

Setup Prime Faces in Tomcat 6.0 or >




Prime Faces is awesome it rocks!! Components are sleek and simple. I haven't had a chance to deploy any real time applications yet but I am eagerly waiting for an opportunity to work with JSF 2.0 and Prime Faces frameworks.

Setting up prime faces is simple and the documentation for prime faces is good and has all the details but I had to do little bit of digging and it took couple of hours to setup. I will try to reduce your work.

1. Download tomcat 6 or greater.
2. Copy jsf-api-2.0.2-FCS.jar and jsf-impl-2.0.2-FCS.jar to TOMCAT_HOME\lib directory for tomcat 6 only. If you have installed tomcat 7 ignore this step. Additionally, to make JSTL-1.2 to work in tomcat6 you need to copy el-api-2.2.jar to your TOMCAT_HOME\lib folder, and the el-impl-2.2.jar to your WEB-INF/lib folder and add below context param to web.xml depending on which JSF implementation (myfaces or sun RI).


3. Download primefaces JSF 2.0 jar file primefaces-2.1-SNAPSHOT.jar
4. From MyEclipse create a new web application project. Make sure you have JDK 5 or greater. I am using JDK 1.6 version.
5. MyEclipse will create web.xml for you. Copy the below the contents to web.xml




















7. Copy all the required jar file to the project classpath and also copy them to WEB-INF\
lib directory of the web application.
8. Deploy your web app.


Update: PrimeFaces had it’s own resource servlet to load resources, now with 2.2.M1 it is gone now since primefaces migrated resource management to JSF 2.0 Resource APIs.

Monday, March 12, 2007

DWR-2.0.rc2 + Spring 2.0 integration

Steps to integrate DWR with Spring 2.0 using new Name Spaces that were introduced. The integration currently works with Spring 2.0 and DWR-2.0RC-2 versions only. This does not work with Spring 2.0.1 or 2.0.2 or 2.0.3 and DWR-2.0RC-2, in order to work with spring 2.0+ versions you need get the current version of DWR-2.0RC-2 from HEAD of the CVS.

Step 1: Download 2.0 version of Spring form http://www.springframework.org/
Step 2: Download DWR version 2.0-rc2 from http://getahead.org/dwr/download

Copy spring and dwr jar files to the webapplication classpath.

Step 3: Add the below lines into web.xml

<servlet>

<servlet-name>dwr-spring</servlet-name>

<servlet-class>

org.springframework.web.servlet.DispatcherServlet

</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>dwr-spring</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>dwr-spring</servlet-name>

<url-pattern>/dwr/*</url-pattern>

</servlet-mapping>

<welcome-file-list>

<!-- index page -->

<welcome-file>index.jsp</welcome-file>

<welcome-file>index.html</welcome-file>

</welcome-file-list>


Step 4: Create the default Spring MVC context file dwr-spring-servlet.xml under WEB-INF directory of the webapplication and configure the DWR

<?xml version="1.0"
encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

http://www.directwebremoting.org/schema/spring-dwr

http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd">


<!-- DWR CONFIGURATION -->

<dwr:configuration>

<dwr:create
javascript="JDate" type="new"

class="java.util.Date" />
</dwr:configuration>




<dwr:controller id="dwrController" debug="true" />

<bean id="dwrHandlerMappings"

class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="alwaysUseFullPath"
value="true" />

<property
name="mappings">

<props>

<prop key="/dwr/**/*">dwrController</prop>

</props>

</property>

</bean>

<!-- END OF DWR CONFIGURATION -->

</beans>

Step 5: Deploy the application in Tomcat 5.5 and point the browser to http://localhost:8080/dwr-spring/dwr/index.html

If the integartion is sucessfull it should display the page with JDate link on the page.

Blow links provides more information on how to integrate DWR and Srping

http://bram.jteam.nl/index.php/2007/01/

PF